Changes 039
CountColor/CountColor.pm 11
CountColor/Makefile.PL 11
DynTest/Makefile.PL 11
FT2/FT2.pm 22
FT2/Makefile.PL 73
FT2/README 010
Flines/Flines.pm 11
Flines/Makefile.PL 11
GIF/GIF.pm 22
GIF/Makefile.PL 84
GIF/README 07
GIF/imgif.c 3926
GIF/t/t30fixed.t 047
ICO/ICO.pm 11
ICO/Makefile.PL 11
ICO/msicon.c 11
Imager.pm 36104
Imager.xs 615
JPEG/JPEG.pm 22
JPEG/Makefile.PL 73
JPEG/README 07
JPEG/imexif.c 11
JPEG/imjpeg.c 015
JPEG/t/t10jpeg.t 225
MANIFEST 313
MANIFEST.SKIP 013
META.yml 105
Makefile.PL 134
Mandelbrot/Makefile.PL 11
Mandelbrot/Mandelbrot.pm 11
PNG/Makefile.PL 73
PNG/PNG.pm 22
PNG/README 07
README 33
SGI/Makefile.PL 11
SGI/SGI.pm 11
T1/Makefile.PL 73
T1/t/t20oo.t 11
TIFF/Makefile.PL 73
TIFF/README 09
TIFF/TIFF.pm 22
TIFF/imtiff.c 11
W32/Makefile.PL 73
W32/README 09
W32/W32.pm 22
W32/win32.c 11
apidocs.perl 15
hlines.c 11
image.c 6040
imager.h 01
imageri.h 02
imext.h 35
imextpl.h 11
img8.c 0616
imgdouble.c 031
lib/Imager/API.pod 11
lib/Imager/APIRef.pod 22
lib/Imager/Draw.pod 11
lib/Imager/ExtUtils.pm 11
lib/Imager/Files.pod 1117
lib/Imager/Filters.pod 11
lib/Imager/Handy.pod 11
lib/Imager/IO.pod 11
lib/Imager/ImageTypes.pod 782
lib/Imager/Inline.pod 11
lib/Imager/LargeSamples.pod 11
lib/Imager/Preprocess.pm 11
lib/Imager/Transformations.pod 11
lib/Imager/Tutorial.pod 11
log.c 1836
log.h 22
paste.im 21
pnm.c 11
quant.c 13150
samples/align-string.pl 11
samples/anaglyph.pl 11
samples/gifscale.pl 11
samples/inline_capture2image.pl 11
samples/interleave.pl 11
samples/samp-scale.cgi 11
samples/samp-tags.cgi 11
samples/slant_text.pl 11
samples/tk-photo.pl 11
t/t01introvert.t 419
t/t020masked.t 17
t/t021sixteen.t 215
t/t022double.t 326
t/t023palette.t 219
t/t05error.t 3430
t/t07iolayer.t 17
t/t1000files.t 17
t/t101nojpeg.t 17
t/t103raw.t 48
t/t104ppm.t 118
t/t107bmp.t 115
t/t15color.t 11
t/t36oofont.t 11
t/t55trans.t 3732
t/t93podcover.t 10
t/t95log.t 034
t/t98meta.t 019
102 files changed (This is a version diff) 9711646
@@ -1,5 +1,44 @@
 Imager release history.  Older releases can be found in Changes.old
 
+Imager 0.84 - 20 Jun 2011
+===========
+
+ - Imager no longer inherits from Exporter (unless you're running an
+   old, old perl.
+
+ - Imager can now write progressive JPEGs (it could always read them).
+   https://rt.cpan.org/Ticket/Display.html?id=68691
+
+Bug fixes:
+
+ - re-work, document and test Imager's logging facility.
+   https://rt.cpan.org/Ticket/Display.html?id=65227
+
+ - fix META.yml testing and the generated META.yml
+   https://rt.cpan.org/Ticket/Display.html?id=65235
+
+ - test and add error reporting to to_*() family methods
+
+ - add to_rgb_double() method.
+   https://rt.cpan.org/Ticket/Display.html?id=65101
+
+ - Imager no longer exports anything by default
+   https://rt.cpan.org/Ticket/Display.html?id=65228
+
+ - convert colors to grayscale if the supplied (or generated) palette
+   contains only grays when performing error diffusion color
+   translation.
+   https://rt.cpan.org/Ticket/Display.html?id=68508
+
+ - writing a paletted image to GIF wouldn't always use the colors
+   supplied (or generated, eg. via make_colors => "mono"), which was
+   confusing at best.
+   https://rt.cpan.org/Ticket/Display.html?id=67912
+
+ - replace (imager|tony)@imager.perl.org in the doc, since I don't
+   plan to continue receiving mail at that address.
+   https://rt.cpan.org/Ticket/Display.html?id=68591
+
 Imager 0.83 - 21 May 2011
 ===========
 
@@ -49,7 +49,7 @@ Imager, Imager::Filter::DynTest
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =cut
 
@@ -11,7 +11,7 @@ my %opts =
   );
 my $MM_ver = eval $ExtUtils::MakeMaker::VERSION;
 if ($MM_ver > 6.06) {
-  $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+  $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
   $opts{ABSTRACT} = 'Color Count an Imager image';
 }
 
@@ -11,7 +11,7 @@ my %opts =
   );
 my $MM_ver = eval $ExtUtils::MakeMaker::VERSION;
 if ($MM_ver > 6.06) {
-  $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+  $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
   $opts{ABSTRACT} = 'Demo Imager filter extension';
 }
 
@@ -5,7 +5,7 @@ use vars qw($VERSION @ISA);
 @ISA = qw(Imager::Font);
 
 BEGIN {
-  $VERSION = "0.79";
+  $VERSION = "0.80";
 
   eval {
     require XSLoader;
@@ -218,7 +218,7 @@ supported font files, so I've renamed it.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -54,12 +54,8 @@ else {
        resources =>
        {
 	homepage => "http://imager.perl.org/",
-	repository =>
-	{
-	 url => "http://imager.perl.org/svn/trunk/Imager/FT2",
-	 web => "http://imager.perl.org/svnweb/public/browse/trunk/Imager/FT2",
-	 type => "svn",
-	},
+	repository => "git://git.imager.perl.org/imager.git",
+	bugtracker => "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager",
        },
       };
     $opts{PREREQ_PM} =
@@ -106,7 +102,7 @@ if ($probe_res) {
     if $probe_res->{LDDLFLAGS};
 
   if ($MM_ver > 6.06) {
-    $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+    $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
     $opts{ABSTRACT} = 'FreeType 2 font driver for Imager';
   }
   
@@ -0,0 +1,10 @@
+Imager::Font::FT2 provides font support to Imager via the FreeType 2
+library.
+
+It requires libfreetype and its development header files to be
+installed.
+
+This is currently shipped as part of Imager, but but Imager may
+install with out installing Imager::Font::FT2, if your module or
+application requires FreeType 2 support then add Imager::Font::FT2 as
+a prerequisite.
@@ -48,7 +48,7 @@ This filter has no parameters.
 
 Original by Arnar M. Hrafnkelsson.
 
-Adapted by Tony Cook <tony@imager.perl.org>
+Adapted by Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -11,7 +11,7 @@ my %opts =
   );
 my $MM_ver = eval $ExtUtils::MakeMaker::VERSION;
 if ($MM_ver > 6.06) {
-  $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+  $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
   $opts{ABSTRACT} = 'Flines Imager filter extension';
 }
 
@@ -4,7 +4,7 @@ use Imager;
 use vars qw($VERSION @ISA);
 
 BEGIN {
-  $VERSION = "0.78";
+  $VERSION = "0.79";
 
   eval {
     require XSLoader;
@@ -121,7 +121,7 @@ Imager's GIF support is documented in L<Imager::Files>.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -38,7 +38,7 @@ else {
   $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ];
 
   # Imager required configure through use
-  my @Imager_req = ( Imager => "0.78" );
+  my @Imager_req = ( Imager => "0.84" );
   if ($MM_ver >= 6.46) {
     $opts{META_MERGE} =
       {
@@ -54,12 +54,8 @@ else {
        resources =>
        {
 	homepage => "http://imager.perl.org/",
-	repository =>
-	{
-	 url => "http://imager.perl.org/svn/trunk/Imager/GIF",
-	 web => "http://imager.perl.org/svnweb/public/browse/trunk/Imager/GIF",
-	 type => "svn",
-	},
+	repository => "git://git.imager.perl.org/imager.git",
+	bugtracker => "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager",
        },
       };
     $opts{PREREQ_PM} =
@@ -92,7 +88,7 @@ if ($probe_res) {
   $opts{INC} = "@inc";
   
   if ($MM_ver > 6.06) {
-    $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+    $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
     $opts{ABSTRACT} = 'GIF Image file support';
   }
   
@@ -0,0 +1,7 @@
+Imager::File::GIF provides GIF file format support for Imager.
+
+It requires libgif to be installed, including development headers.
+
+This is currently shipped as part of Imager, but Imager may install
+with out installing Imager::File::GIF, so if you need GIF support, add
+a dependency on Imager::File::GIF.
@@ -1229,23 +1229,26 @@ in_palette(i_color *c, i_quantize *quant, int size) {
 }
 
 /*
-=item has_common_palette(imgs, count, quant, want_trans)
+=item has_common_palette(imgs, count, quant)
 
-Tests if all the given images are paletted and have a common palette,
-if they do it builds that palette.
+Tests if all the given images are paletted and their colors are in the
+palette produced.
 
-A possible improvement might be to eliminate unused colors in the
-images palettes.
+Previously this would build a consolidated palette from the source,
+but that meant that if the caller supplied a static palette (or
+specified a fixed palette like "webmap") then we wouldn't be
+quantizing to the caller specified palette.
 
 =cut
 */
+
 static int
-has_common_palette(i_img **imgs, int count, i_quantize *quant, 
-                   int want_trans) {
+has_common_palette(i_img **imgs, int count, i_quantize *quant) {
   int size = quant->mc_count;
   int i;
   int imgn;
   char used[256];
+  int col_count;
 
   /* we try to build a common palette here, if we can manage that, then
      that's the palette we use */
@@ -1277,25 +1280,22 @@ has_common_palette(i_img **imgs, int count, i_quantize *quant,
       memset(used, 1, sizeof(used));
     }
 
-    for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
+    col_count = i_colorcount(imgs[imgn]);
+    for (i = 0; i < col_count; ++i) {
       i_color c;
       
       i_getcolors(imgs[imgn], i, &c, 1);
       if (used[i]) {
-        if (in_palette(&c, quant, size) < 0) {
-          if (size < quant->mc_size) {
-            quant->mc_colors[size++] = c;
-          }
-          else {
-            /* oops, too many colors */
-            return 0;
-          }
+        if (in_palette(&c, quant, quant->mc_count) < 0) {
+	  mm_log((1, "  color not found in palette, no palette shortcut\n"));
+  
+	  return 0;
         }
       }
     }
   }
 
-  quant->mc_count = size;
+  mm_log((1, "  all colors found in palette, palette shortcut\n"));
 
   return 1;
 }
@@ -1424,13 +1424,9 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
       mm_log((2, "  reserving color for transparency\n"));
       --quant->mc_size;
     }
-    if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
-      glob_paletted = 1;
-    }
-    else {
-      glob_paletted = 0;
-      i_quant_makemap(quant, glob_imgs, glob_img_count);
-    }
+
+    i_quant_makemap(quant, glob_imgs, glob_img_count);
+    glob_paletted = has_common_palette(glob_imgs, glob_img_count, quant);
     glob_color_count = quant->mc_count;
     quant->mc_colors = orig_colors;
   }
@@ -1451,14 +1447,10 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
   }
   else {
     want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
-    if (has_common_palette(imgs, 1, quant, want_trans)) {
-      colors_paletted = 1;
-    }
-    else {
-      colors_paletted = 0;
-      i_quant_makemap(quant, imgs, 1);
-    }
+    i_quant_makemap(quant, imgs, 1);
+    colors_paletted = has_common_palette(imgs, 1, quant);
   }
+
   if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
     myfree(glob_colors);
     myfree(localmaps);
@@ -1525,13 +1517,8 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
          to give room for a transparency colour */
       if (want_trans && quant->mc_size == 256)
         --quant->mc_size;
-      if (has_common_palette(imgs, 1, quant, want_trans)) {
-        colors_paletted = 1;
-      }
-      else {
-        colors_paletted = 0;
-        i_quant_makemap(quant, imgs, 1);
-      }
+      i_quant_makemap(quant, imgs, 1);
+      colors_paletted = has_common_palette(imgs, 1, quant);
       if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
 	myfree(glob_colors);
 	myfree(localmaps);
@@ -1639,7 +1626,7 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
       if (want_trans && quant->mc_size == 256)
 	--quant->mc_size;
 
-      if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
+      if (has_common_palette(imgs+imgn, 1, quant)) {
         result = quant_paletted(quant, imgs[imgn]);
       }
       else {
@@ -0,0 +1,47 @@
+#!perl -w
+use strict;
+use Test::More tests => 9;
+use Imager;
+use Imager::Test qw(test_image);
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t30fixed.log");
+
+{
+  # RT 67912
+  # previously, if you tried to write a paletted image to GIF:
+  #  - specified a fixed palette with make_colors => "mono", "web" or "none"
+  #  - there was room for the colors in the image in the rest of the
+  #  palette (or they were found in the generated palette)
+  # the GIF would be written with essentially it's original palette
+  # instead of the specified palette
+  #
+  # This was confusing, especially if you specified a restricted
+  # palette such as mono or a small greyscale ramp
+
+  my $src = test_image();
+  ok($src, "make source image");
+  my $pal = $src->to_paletted(max_colors => 250);
+  ok($pal, "make paletted version");
+  cmp_ok($pal->colorcount, "<=", 250, "make sure not too many colors");
+
+  my $mono = $src->to_paletted(make_colors => "mono", translate => "errdiff");
+  ok($mono, "make mono image directly");
+  ok($mono->write(file => "testout/t30monodirect.gif", type => "gif"),
+     "save mono direct image");
+
+  Imager->log("Save manually paletted version\n");
+  ok($pal->write(file => "testout/t30color.gif"),
+     "save generated palette version");
+  Imager->log("Save mono version\n");
+  ok($pal->write(file => "testout/t30monoind.gif", type => "gif",
+		 make_colors => "mono", translate => "errdiff"),
+     "write paletted with mono colormap");
+
+  my $rd = Imager->new(file => "testout/t30monoind.gif", type => "gif");
+  ok($rd, "read it back in");
+  is($rd->colorcount, 2, "should only have 2 colors");
+}
+
+Imager->close_log;
@@ -167,7 +167,7 @@ Imager's MS Icon support is documented in L<Imager::Files>.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -12,7 +12,7 @@ my %opts =
   );
 my $MM_ver = eval $ExtUtils::MakeMaker::VERSION;
 if ($MM_ver > 6.06) {
-  $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+  $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
   $opts{ABSTRACT} = 'Icon Image file support';
 }
 
@@ -1533,7 +1533,7 @@ write_mask(i_io_glue_t *ig, ico_image_t const *image, int *error) {
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION
 
@@ -94,14 +94,6 @@ use Imager::Font;
 );
 
 @EXPORT=qw(
-	   init_log
-	   i_list_formats
-	   i_has_format
-	   malloc_state
-	   i_color_new
-
-	   i_img_empty
-	   i_img_empty_ch
 	  );
 
 %EXPORT_TAGS=
@@ -145,8 +137,11 @@ my %defaults;
 
 BEGIN {
   require Exporter;
-  @ISA = qw(Exporter);
-  $VERSION = '0.83';
+  my $ex_version = eval $Exporter::VERSION;
+  if ($ex_version < 5.57) {
+    @ISA = qw(Exporter);
+  }
+  $VERSION = '0.84';
   eval {
     require XSLoader;
     XSLoader::load(Imager => $VERSION);
@@ -451,15 +446,14 @@ sub import {
 }
 
 sub init_log {
-  i_init_log($_[0],$_[1]);
-  i_log_entry("Imager $VERSION starting\n", 1);
+  Imager->open_log(log => $_[0], level => $_[1]);
 }
 
 
 sub init {
   my %parms=(loglevel=>1,@_);
   if ($parms{'log'}) {
-    init_log($parms{'log'},$parms{'loglevel'});
+    Imager->open_log(log => $parms{log}, level => $parms{loglevel});
   }
 
   if (exists $parms{'warn_obsolete'}) {
@@ -473,6 +467,42 @@ sub init {
   }
 }
 
+{
+  my $is_logging = 0;
+
+  sub open_log {
+    my $class = shift;
+    my (%opts) = ( loglevel => 1, @_ );
+
+    $is_logging = i_init_log($opts{log}, $opts{loglevel});
+    unless ($is_logging) {
+      Imager->_set_error(Imager->_error_as_msg());
+      return;
+    }
+
+    Imager->log("Imager $VERSION starting\n", 1);
+
+    return $is_logging;
+  }
+
+  sub close_log {
+    i_init_log(undef, -1);
+    $is_logging = 0;
+  }
+
+  sub log {
+    my ($class, $message, $level) = @_;
+
+    defined $level or $level = 1;
+
+    i_log_entry($message, $level);
+  }
+
+  sub is_logging {
+    return $is_logging;
+  }
+}
+
 END {
   if ($DEBUG) {
     print "shutdown code\n";
@@ -931,24 +961,21 @@ sub to_paletted {
     return;
   }
 
-  my $result = Imager->new;
-  $result->{IMG} = i_img_to_pal($self->{IMG}, $opts);
-
-  #print "Type ", i_img_type($result->{IMG}), "\n";
+  $self->_valid_image
+    or return;
 
-  if ($result->{IMG}) {
-    return $result;
-  }
-  else {
-    $self->{ERRSTR} = $self->_error_as_msg;
+  my $result = Imager->new;
+  unless ($result->{IMG} = i_img_to_pal($self->{IMG}, $opts)) {
+    $self->_set_error(Imager->_error_as_msg);
     return;
   }
+
+  return $result;
 }
 
-# convert a paletted (or any image) to an 8-bit/channel RGB images
+# convert a paletted (or any image) to an 8-bit/channel RGB image
 sub to_rgb8 {
   my $self = shift;
-  my $result;
 
   unless (defined wantarray) {
     my @caller = caller;
@@ -956,30 +983,57 @@ sub to_rgb8 {
     return;
   }
 
-  if ($self->{IMG}) {
-    $result = Imager->new;
-    $result->{IMG} = i_img_to_rgb($self->{IMG})
-      or undef $result;
+  $self->_valid_image
+    or return;
+
+  my $result = Imager->new;
+  unless ($result->{IMG} = i_img_to_rgb($self->{IMG})) {
+    $self->_set_error(Imager->_error_as_msg());
+    return;
   }
 
   return $result;
 }
 
-# convert a paletted (or any image) to an 8-bit/channel RGB images
+# convert a paletted (or any image) to a 16-bit/channel RGB image
 sub to_rgb16 {
   my $self = shift;
-  my $result;
 
   unless (defined wantarray) {
     my @caller = caller;
-    warn "to_rgb16() called in void context - to_rgb8() returns the converted image at $caller[1] line $caller[2]\n";
+    warn "to_rgb16() called in void context - to_rgb16() returns the converted image at $caller[1] line $caller[2]\n";
     return;
   }
 
-  if ($self->{IMG}) {
-    $result = Imager->new;
-    $result->{IMG} = i_img_to_rgb16($self->{IMG})
-      or undef $result;
+  $self->_valid_image
+    or return;
+
+  my $result = Imager->new;
+  unless ($result->{IMG} = i_img_to_rgb16($self->{IMG})) {
+    $self->_set_error(Imager->_error_as_msg());
+    return;
+  }
+
+  return $result;
+}
+
+# convert a paletted (or any image) to an double/channel RGB image
+sub to_rgb_double {
+  my $self = shift;
+
+  unless (defined wantarray) {
+    my @caller = caller;
+    warn "to_rgb16() called in void context - to_rgb_double() returns the converted image at $caller[1] line $caller[2]\n";
+    return;
+  }
+
+  $self->_valid_image
+    or return;
+
+  my $result = Imager->new;
+  unless ($result->{IMG} = i_img_to_drgb($self->{IMG})) {
+    $self->_set_error(Imager->_error_as_msg());
+    return;
   }
 
   return $result;
@@ -4253,6 +4307,9 @@ box() - L<Imager::Draw/box()> - draw a filled or outline box.
 
 circle() - L<Imager::Draw/circle()> - draw a filled circle
 
+close_log() - L<Imager::ImageTypes/close_log()> - close the Imager
+debugging log.
+
 colorcount() - L<Imager::ImageTypes/colorcount()> - the number of
 colors in an image's palette (paletted images only)
 
@@ -4336,10 +4393,16 @@ is_bilevel() - L<Imager::ImageTypes/is_bilevel()> - returns whether
 image write functions should write the image in their bilevel (blank
 and white, no gray levels) format
 
+is_logging() L<Imager::ImageTypes/is_logging()> - test if the debug
+log is active.
+
 line() - L<Imager::Draw/line()> - draw an interval
 
 load_plugin() - L<Imager::Filters/load_plugin()>
 
+log() - L<Imager::ImageTypes/log()> - send a message to the debugging
+log.
+
 map() - L<Imager::Transformations/"Color Mappings"> - remap color
 channel values
 
@@ -4365,6 +4428,8 @@ NF() - L<Imager::Handy/NF()>
 
 open() - L<Imager::Files> - an alias for read()
 
+open_log() - L<Imager::ImageTypes/open_log()> - open the debug log.
+
 =for stopwords IPTC
 
 parseiptc() - L<Imager::Files/parseiptc()> - parse IPTC data from a JPEG
@@ -4431,6 +4496,9 @@ to_rgb16() - L<Imager::ImageTypes/to_rgb16()>
 
 to_rgb8() - L<Imager::ImageTypes/to_rgb8()>
 
+to_rgb_double() - L<Imager::ImageTypes/to_rgb_double()> - convert to
+double per sample image.
+
 transform() - L<Imager::Engines/"transform()">
 
 transform2() - L<Imager::Engines/"transform2()">
@@ -4701,7 +4769,7 @@ it will be delayed until I get a chance to write them.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org> is the current maintainer for Imager.
+Tony Cook <tonyc@cpan.org> is the current maintainer for Imager.
 
 Arnar M. Hrafnkelsson is the original author of Imager.
 
@@ -862,10 +862,11 @@ validate_i_ppal(i_img *im, i_palidx const *indexes, int count) {
 #define ICLF_new_internal(r, g, b, a) i_fcolor_new((r), (g), (b), (a))
 #define ICLF_DESTROY(cl) i_fcolor_destroy(cl)
 
-
-/* the m_init_log() function was called init_log(), renamed to reduce
-    potential naming conflicts */
-#define init_log m_init_log
+#ifdef IMAGER_LOG
+#define i_log_enabled() 1
+#else
+#define i_log_enabled() 0
+#endif
 
 #if i_int_hlines_testing()
 
@@ -1325,20 +1326,24 @@ i_sametype_chans(im, x, y, channels)
                int y
                int channels
 
-void
+int
 i_init_log(name_sv,level)
 	      SV*    name_sv
 	       int     level
 	PREINIT:
 	  const char *name = SvOK(name_sv) ? SvPV_nolen(name_sv) : NULL;
 	CODE:
-	  i_init_log(name, level);
+	  RETVAL = i_init_log(name, level);
+	OUTPUT:
+	  RETVAL
 
 void
 i_log_entry(string,level)
 	      char*    string
 	       int     level
 
+int
+i_log_enabled()
 
 void
 i_img_exorcise(im)
@@ -3524,6 +3529,10 @@ i_img_double_new(x, y, ch)
         int y
         int ch
 
+Imager::ImgRaw
+i_img_to_drgb(im)
+       Imager::ImgRaw im
+
 undef_int
 i_tags_addn(im, name, code, idata)
         Imager::ImgRaw im
@@ -4,7 +4,7 @@ use Imager;
 use vars qw($VERSION @ISA);
 
 BEGIN {
-  $VERSION = "0.78";
+  $VERSION = "0.79";
 
   eval {
     require XSLoader;
@@ -80,7 +80,7 @@ Imager's JPEG support is documented in L<Imager::Files>.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -54,12 +54,8 @@ else {
        resources =>
        {
 	homepage => "http://imager.perl.org/",
-	repository =>
-	{
-	 url => "http://imager.perl.org/svn/trunk/Imager/JPEG",
-	 web => "http://imager.perl.org/svnweb/public/browse/trunk/Imager/JPEG",
-	 type => "svn",
-	},
+	repository => "git://git.imager.perl.org/imager.git",
+	bugtracker => "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager",
        },
       };
     $opts{PREREQ_PM} =
@@ -92,7 +88,7 @@ if ($probe_res) {
   $opts{INC} = "@inc";
   
   if ($MM_ver > 6.06) {
-    $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+    $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
     $opts{ABSTRACT} = 'JPEG Image file support';
   }
   
@@ -0,0 +1,7 @@
+Imager::File::JPEG provides JPEG file format support for Imager.
+
+It requires libjpeg to be installed.
+
+This is currently shipped as part of Imager, but Imager may install
+with out installing Imager::File::JPEG, so if you need JPEG support,
+add a dependency on Imager::File::JPEG.
@@ -1576,7 +1576,7 @@ http://www.exif.org/
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION
 
@@ -543,6 +543,14 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
     i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
   }
 
+  /* I originally used jpeg_has_multiple_scans() here, but that can
+   * return true for non-progressive files too.  The progressive_mode
+   * member is available at least as far back as 6b and does the right
+   * thing.
+   */
+  i_tags_setn(&im->tags, "jpeg_progressive", 
+	      cinfo.progressive_mode ? 1 : 0);
+
   (void) jpeg_finish_decompress(&cinfo);
   jpeg_destroy_decompress(&cinfo);
   *itlength=tlength;
@@ -567,6 +575,7 @@ i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
   double xres, yres;
   int comment_entry;
   int want_channels = im->channels;
+  int progressive = 0;
 
   struct jpeg_compress_struct cinfo;
   struct my_error_mgr jerr;
@@ -618,6 +627,12 @@ i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
   jpeg_set_defaults(&cinfo);
   jpeg_set_quality(&cinfo, quality, TRUE);  /* limit to baseline-JPEG values */
 
+  if (!i_tags_get_int(&im->tags, "jpeg_progressive", 0, &progressive))
+    progressive = 0;
+  if (progressive) {
+    jpeg_simple_progression(&cinfo);
+  }
+
   got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
   got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
   if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
@@ -2,7 +2,7 @@
 use strict;
 use Imager qw(:all);
 use Test::More;
-use Imager::Test qw(is_color_close3 test_image_raw);
+use Imager::Test qw(is_color_close3 test_image_raw test_image is_image);
 
 -d "testout" or mkdir "testout";
 
@@ -11,7 +11,7 @@ init_log("testout/t101jpeg.log",1);
 $Imager::formats{"jpeg"}
   or plan skip_all => "no jpeg support";
 
-plan tests => 94;
+plan tests => 101;
 
 my $green=i_color_new(0,255,0,255);
 my $blue=i_color_new(0,0,255,255);
@@ -408,4 +408,27 @@ SKIP:
   ok(grep($_ eq 'jpeg', Imager->write_types), "check jpeg in write types");
 }
 
+{ # progressive JPEG
+  # https://rt.cpan.org/Ticket/Display.html?id=68691
+  my $im = test_image();
+  my $progim = $im->copy;
 
+  ok($progim->write(file => "testout/t10prog.jpg", type => "jpeg",
+		    jpeg_progressive => 1),
+     "write progressive jpeg");
+
+  my $rdprog = Imager->new(file => "testout/t10prog.jpg");
+  ok($rdprog, "read progressive jpeg");
+  my @prog = $rdprog->tags(name => "jpeg_progressive");
+  is($prog[0], 1, "check progressive flag set on read");
+
+  my $data;
+  ok($im->write(data => \$data, type => "jpeg"), 
+     "save as non-progressive to compare");
+  my $norm = Imager->new(data => $data);
+  ok($norm, "read non-progressive file");
+  my @nonprog = $norm->tags(name => "jpeg_progressive");
+  is($nonprog[0], 0, "check progressive flag 0 for non prog file");
+
+  is_image($rdprog, $norm, "prog vs norm should be the same image");
+}
@@ -66,6 +66,7 @@ FT2/FT2.pm
 FT2/FT2.xs
 FT2/imft2.h
 FT2/Makefile.PL
+FT2/README
 FT2/t/t10ft2.t
 FT2/typemap
 gaussian.im
@@ -74,8 +75,10 @@ GIF/GIF.xs
 GIF/imgif.c
 GIF/imgif.h
 GIF/Makefile.PL
+GIF/README
 GIF/t/t10gif.t
 GIF/t/t20new.t
+GIF/t/t30fixed.t
 GIF/testimg/badindex.gif	GIF with a bad color index
 GIF/testimg/bandw.gif
 GIF/testimg/expected.gif
@@ -132,7 +135,8 @@ imextdef.h
 imextpl.h			Included by external modules for Perl API access
 imextpltypes.h			Define Perl API function table type
 imexttypes.h			Define API function table type
-img16.c
+img16.c				Implements 16-bit/sample images
+img8.c				Implements 8-bit/sample images
 imgdouble.c			Implements double/sample images
 imio.h
 immacros.h
@@ -150,6 +154,7 @@ JPEG/imjpeg.h
 JPEG/JPEG.pm
 JPEG/JPEG.xs
 JPEG/Makefile.PL
+JPEG/README
 JPEG/t/t00load.t
 JPEG/t/t10jpeg.t		Test jpeg support
 JPEG/testimg/209_yonge.jpg	Regression test: #17981
@@ -175,7 +180,7 @@ lib/Imager/Font/BBox.pm
 lib/Imager/Font/FreeType2.pm
 lib/Imager/Font/Image.pm
 lib/Imager/Font/Truetype.pm
-lib/Imager/Font/Type1.pm	Compatibilty wrapper for Imager::Font::T1
+lib/Imager/Font/Type1.pm	Compatibility wrapper for Imager::Font::T1
 lib/Imager/Font/Wrap.pm
 lib/Imager/Fountain.pm
 lib/Imager/Handy.pod
@@ -206,7 +211,6 @@ MANIFEST
 MANIFEST.SKIP
 map.c
 maskimg.c
-META.yml			Module meta-data
 palimg.c
 paste.im
 plug.h
@@ -215,6 +219,7 @@ PNG/impng.h
 PNG/Makefile.PL
 PNG/PNG.pm
 PNG/PNG.xs
+PNG/README
 PNG/t/00load.t
 PNG/t/10png.t			Test png support
 PNG/testimg/palette.png
@@ -326,6 +331,8 @@ t/t91pod.t			Test POD with Test::Pod
 t/t92samples.t
 t/t93podcover.t			POD Coverage tests
 t/t94kwalitee.t			Various "kwalitee" tests
+t/t95log.t
+t/t98meta.t
 t/t99thread.t			Test wrt to perl threads
 t/tr18561.t			Regression tests
 t/tr18561b.t
@@ -419,6 +426,7 @@ tga.c				Reading and writing Targa files
 TIFF/imtiff.c
 TIFF/imtiff.h
 TIFF/Makefile.PL
+TIFF/README
 TIFF/t/t10tiff.t		Test tiff support
 TIFF/testimg/alpha.tif		Alpha scaling test image
 TIFF/testimg/comp4.bmp		Compressed 4-bit/pixel BMP
@@ -457,7 +465,9 @@ W32/fontfiles/ExistenceTest.ttf
 W32/imw32.h
 W32/lib/Imager/Font/Win32.pm
 W32/Makefile.PL
+W32/README
 W32/t/t10win32.t		Tests Win32 GDI font support
 W32/W32.pm
 W32/W32.xs
 W32/win32.c			Implements font support through Win32 GDI
+META.yml                                 Module meta-data (added by MakeMaker)
@@ -63,6 +63,7 @@ Makefile\.old$
 ^SGI/testout/
 ^TIFF/testout/
 ^T1/testout/
+^W32/testout/
 
 # generated from .xs
 ^CountColor/CountColor\.c$
@@ -76,9 +77,12 @@ Makefile\.old$
 ^SGI/SGI\.c$
 ^TIFF/TIFF\.c$
 ^T1/T1\.c$
+^W32/W32\.c$
 
 ^.*/Changes$
 ^.*/MANIFEST(\.SKIP)?$
+^.+/inc/Devel/CheckLib\.pm$
+^.+/blib/
 ^blib/
 ^Flines/Flines\.c$
 ^Imager\.c$
@@ -113,3 +117,12 @@ Makefile\.old$
 ^samples/logo$
 ^samples/transform\.pl$
 ^samples/transform1\.ppm$
+
+# Win32 junk
+^(.+/)?dll\.(base|exp)$
+^(.+/)?\w+\.def$
+^(.+/)?\w+\_def.old$
+\.exe$
+
+# sub-module build junk
+\.bak$
@@ -1,9 +1,9 @@
 --- #YAML:1.0
 name:               Imager
-version:            0.83
+version:            0.84
 abstract:           Perl extension for Generating 24 bit Images
 author:
-    - Tony Cook <tony@imager.perl.org>, Arnar M. Hrafnkelsson
+    - Tony Cook <tonyc@cpan.org>, Arnar M. Hrafnkelsson
 license:            perl
 distribution_type:  module
 configure_requires:
@@ -13,14 +13,9 @@ build_requires:
 requires:
     Test::More:  0.47
 resources:
-    bugtracker:
-        mailto:  bug-Imager@rt.cpan.org
-        web:     http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager
-    homepage:  http://imager.perl.org/
-    repository:
-        type:  git
-        url:   git://git.imager.perl.org/imager.git
-        web:   http://git.imager.perl.org/imager.git
+    bugtracker:  http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager
+    homepage:    http://imager.perl.org/
+    repository:  git://git.imager.perl.org/imager.git
 no_index:
     directory:
         - t
@@ -163,7 +163,7 @@ my @objs = qw(Imager.o draw.o polygon.o image.o io.o iolayer.o
               log.o gaussian.o conv.o pnm.o raw.o feat.o font.o combine.o
               filters.o dynaload.o stackmach.o datatypes.o
               regmach.o trans2.o quant.o error.o convert.o
-              map.o tags.o palimg.o maskimg.o img16.o rotate.o
+              map.o tags.o palimg.o maskimg.o img8.o img16.o rotate.o
               bmp.o tga.o color.o fills.o imgdouble.o limits.o hlines.o
               imext.o scale.o rubthru.o render.o paste.o compose.o flip.o);
 
@@ -192,7 +192,7 @@ if ($coverage) {
 # eval to prevent warnings about versions with _ in them
 my $MM_ver = eval $ExtUtils::MakeMaker::VERSION;
 if ($MM_ver > 6.06) {
-  $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>, Arnar M. Hrafnkelsson';
+  $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>, Arnar M. Hrafnkelsson';
   $opts{ABSTRACT} = 'Perl extension for Generating 24 bit Images';
 }
 
@@ -220,17 +220,8 @@ if ($MM_ver >= 6.46) {
      resources =>
      {
       homepage => "http://imager.perl.org/",
-      repository =>
-      {
-       url => "git://git.imager.perl.org/imager.git",
-       web => "http://git.imager.perl.org/imager.git",
-       type => "git",
-      },
-      bugtracker =>
-      {
-       web => "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager",
-       mailto => 'bug-Imager@rt.cpan.org',
-      },
+      repository => "git://git.imager.perl.org/imager.git",
+      bugtracker => "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager",
      },
     };
 }
@@ -11,7 +11,7 @@ my %opts =
   );
 my $MM_ver = eval $ExtUtils::MakeMaker::VERSION;
 if ($MM_ver > 6.06) {
-  $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+  $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
   $opts{ABSTRACT} = 'Mandelbrot Imager filter extension';
 }
 
@@ -82,7 +82,7 @@ if the sequence tend towards infinity.
 
 Original by Arnar M. Hrafnkelsson.
 
-Adapted and expanded by Tony Cook <tony@imager.perl.org>
+Adapted and expanded by Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -54,12 +54,8 @@ else {
        resources =>
        {
 	homepage => "http://imager.perl.org/",
-	repository =>
-	{
-	 url => "http://imager.perl.org/svn/trunk/Imager/PNG",
-	 web => "http://imager.perl.org/svnweb/public/browse/trunk/Imager/PNG",
-	 type => "svn",
-	},
+	repository => "git://git.imager.perl.org/imager.git",
+	bugtracker => "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager",
        },
       };
     $opts{PREREQ_PM} =
@@ -112,7 +108,7 @@ if ($probe_res) {
   $opts{INC} = "@inc";
   
   if ($MM_ver > 6.06) {
-    $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+    $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
     $opts{ABSTRACT} = 'PNG Image file support';
   }
   
@@ -4,7 +4,7 @@ use Imager;
 use vars qw($VERSION @ISA);
 
 BEGIN {
-  $VERSION = "0.78";
+  $VERSION = "0.79";
 
   eval {
     require XSLoader;
@@ -74,7 +74,7 @@ Imager's PNG support is documented in L<Imager::Files>.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -0,0 +1,7 @@
+Imager::File::PNG provides PNG file format support for Imager.
+
+It requires libpng to be installed.
+
+This is currently shipped as part of Imager, but Imager may install
+with out installing Imager::File::PNG, so if you need PNG support,
+add a dependency on Imager::File::PNG.
@@ -1,6 +1,6 @@
 ================================================================
 Copyright (c) 1999-2004 Arnar M. Hrafnkelsson. All rights reserved.
-Copyright (c) 2004-2010 Anthony Cook.
+Copyright (c) 2004-2011 Anthony Cook.
 This program is free software; you can redistribute it and/or
 modify it under the same terms as Perl itself.
 
@@ -11,7 +11,7 @@ copyrighted by Adobe.  See adobe.txt for license information.
 >> THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY WHATSOEVER <<
 
 If you like or hate Imager, please let me know by sending mail 
-to imager@imager.perl.org - I love feedback.
+to tonyc@cpan.org - I love feedback.
 
 ================================================================
 
@@ -75,7 +75,7 @@ If either fails do take a peek at the file errep.perl.  It's creates a
 file report.txt.  This is some information which will help me discover
 where the problem is so I can try to fix it in future releases.  If
 you find running it ok (just remember - no warranty!) please send the
-report.txt via email to imager@imager.perl.org.
+report.txt via email to tonyc@cpan.org.
 
 Troubleshooting tips:
 
@@ -12,7 +12,7 @@ my %opts =
   );
 my $MM_ver = eval $ExtUtils::MakeMaker::VERSION;
 if ($MM_ver > 6.06) {
-  $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+  $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
   $opts{ABSTRACT} = 'SGI Image file support';
 }
 
@@ -80,7 +80,7 @@ Imager's MS Icon support is documented in L<Imager::Files>.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -54,12 +54,8 @@ else {
        resources =>
        {
 	homepage => "http://imager.perl.org/",
-	repository =>
-	{
-	 url => "git://git.imager.perl.org/imager-type1.git",
-	 web => "http://git.imager.perl.org/imager-type1.git/",
-	 type => "git",
-	},
+	repository => "git://git.imager.perl.org/imager.git",
+	bugtracker => "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager",
        },
       };
     $opts{PREREQ_PM} =
@@ -93,7 +89,7 @@ if ($probe_res) {
   $opts{INC} = "@inc";
 
   if ($MM_ver > 6.06) {
-    $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+    $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
     $opts{ABSTRACT} = 'T1Lib font driver for Imager';
   }
   
@@ -14,7 +14,7 @@ die $Imager::ERRSTR unless $red;
 
 ok((-d "testout" or mkdir "testout"), "make output directory");
 
-init_log("testout/t20oo.log", 1);
+Imager::init_log("testout/t20oo.log", 1);
 
 my $img=Imager->new(xsize=>300, ysize=>100) or die "$Imager::ERRSTR\n";
 
@@ -54,12 +54,8 @@ else {
        resources =>
        {
 	homepage => "http://imager.perl.org/",
-	repository =>
-	{
-	 url => "http://imager.perl.org/svn/trunk/Imager/TIFF",
-	 web => "http://imager.perl.org/svnweb/public/browse/trunk/Imager/TIFF",
-	 type => "svn",
-	},
+	repository => "git://git.imager.perl.org/imager.git",
+	bugtracker => "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager",
        },
       };
     $opts{PREREQ_PM} =
@@ -92,7 +88,7 @@ if ($probe_res) {
   $opts{INC} = "@inc";
 
   if ($MM_ver > 6.06) {
-    $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+    $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
     $opts{ABSTRACT} = 'TIFF image file support for Imager';
   }
   
@@ -0,0 +1,9 @@
+Imager::File::TIFF provides TIFF file format support for Imager.
+
+It requires libtiff to be installed.
+
+This is currently shipped as part of Imager, but Imager may install
+with out installing Imager::File::TIFF, so if you need TIFF support,
+add a dependency on Imager::File::TIFF.
+
+Makefile.PL will reject libtiff 3.9.0 due to a bug in that release.
\ No newline at end of file
@@ -4,7 +4,7 @@ use Imager;
 use vars qw($VERSION @ISA);
 
 BEGIN {
-  $VERSION = "0.78";
+  $VERSION = "0.79";
 
   eval {
     require XSLoader;
@@ -131,7 +131,7 @@ Imager's TIFF support is documented in L<Imager::Files>.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -2550,7 +2550,7 @@ myTIFFIsCODECConfigured(uint16 scheme) {
 
 =head1 AUTHOR
 
-Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook <tony@imager.perl.org>
+Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -54,12 +54,8 @@ else {
        resources =>
        {
 	homepage => "http://imager.perl.org/",
-	repository =>
-	{
-	 url => "http://imager.perl.org/svn/trunk/Imager/W32",
-	 web => "http://imager.perl.org/svnweb/public/browse/trunk/Imager/W32",
-	 type => "svn",
-	},
+	repository => "git://git.imager.perl.org/imager.git",
+	bugtracker => "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager",
        },
       };
     $opts{PREREQ_PM} =
@@ -92,7 +88,7 @@ if ($probe_res) {
   $opts{INC} = "@inc";
 
   if ($MM_ver > 6.06) {
-    $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+    $opts{AUTHOR} = 'Tony Cook <tonyc@cpan.org>';
     $opts{ABSTRACT} = 'Win32 font file support for Imager';
   }
   
@@ -0,0 +1,9 @@
+Imager::Font::W32 provides font support to Imager via the Win32 GDI
+API functions..
+
+It requires Win32 header files and libraries to be available.
+
+This is currently shipped as part of Imager, but may not be installed
+as part of Imager if headers or libraries aren't found at build time.,
+if your module or application requires Win32 font support support then
+add Imager::Font::W32 as a prerequisite.
@@ -5,7 +5,7 @@ use vars qw($VERSION @ISA);
 @ISA = qw(Imager::Font);
 
 BEGIN {
-  $VERSION = "0.78";
+  $VERSION = "0.79";
 
   eval {
     require XSLoader;
@@ -85,7 +85,7 @@ renamed it.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -55,7 +55,7 @@ int i_wf_bbox(const char *face, int size, const char *text, int length, int *bbo
   MAT2 mat;
   int ascent, descent, max_ascent = -size, min_descent = size;
   const char *workp;
-  int work_len;
+  size_t work_len;
   int got_first_ch = 0;
   unsigned long first_ch, last_ch;
 
@@ -79,6 +79,10 @@ for my $file (@files) {
 open OUT, "> $outname"
   or die "Cannot open $outname: $!";
 
+# I keep this file in git and as part of the dist, make sure newlines
+# don't mess me up
+binmode OUT;
+
 print OUT <<'EOS';
 Do not edit this file, it is generated automatically by apidocs.perl
 from Imager's source files.
@@ -162,7 +166,7 @@ print OUT <<'EOS';
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -353,7 +353,7 @@ i_int_hlines_fill_fill(i_img *im, i_int_hlines *hlines, i_fill_t *fill) {
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION
 
@@ -39,17 +39,6 @@ Some of these functions are internal.
 /* Hack around an obscure linker bug on solaris - probably due to builtin gcc thingies */
 static void fake(void) { ceil(1); }
 
-static int i_ppix_d(i_img *im, int x, int y, const i_color *val);
-static int i_gpix_d(i_img *im, int x, int y, i_color *val);
-static int i_glin_d(i_img *im, int l, int r, int y, i_color *vals);
-static int i_plin_d(i_img *im, int l, int r, int y, const i_color *vals);
-static int i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val);
-static int i_gpixf_d(i_img *im, int x, int y, i_fcolor *val);
-static int i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals);
-static int i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals);
-static int i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, const int *chans, int chan_count);
-static int i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, const int *chans, int chan_count);
-
 /*
 =item i_img_alloc()
 =category Image Implementation
@@ -242,213 +231,6 @@ void i_fcolor_destroy(i_fcolor *cl) {
   myfree(cl);
 }
 
-/*
-=item IIM_base_8bit_direct (static)
-
-A static i_img object used to initialize direct 8-bit per sample images.
-
-=cut
-*/
-static i_img IIM_base_8bit_direct =
-{
-  0, /* channels set */
-  0, 0, 0, /* xsize, ysize, bytes */
-  ~0U, /* ch_mask */
-  i_8_bits, /* bits */
-  i_direct_type, /* type */
-  0, /* virtual */
-  NULL, /* idata */
-  { 0, 0, NULL }, /* tags */
-  NULL, /* ext_data */
-
-  i_ppix_d, /* i_f_ppix */
-  i_ppixf_d, /* i_f_ppixf */
-  i_plin_d, /* i_f_plin */
-  i_plinf_d, /* i_f_plinf */
-  i_gpix_d, /* i_f_gpix */
-  i_gpixf_d, /* i_f_gpixf */
-  i_glin_d, /* i_f_glin */
-  i_glinf_d, /* i_f_glinf */
-  i_gsamp_d, /* i_f_gsamp */
-  i_gsampf_d, /* i_f_gsampf */
-
-  NULL, /* i_f_gpal */
-  NULL, /* i_f_ppal */
-  NULL, /* i_f_addcolors */
-  NULL, /* i_f_getcolors */
-  NULL, /* i_f_colorcount */
-  NULL, /* i_f_maxcolors */
-  NULL, /* i_f_findcolor */
-  NULL, /* i_f_setcolors */
-
-  NULL, /* i_f_destroy */
-
-  i_gsamp_bits_fb,
-  NULL, /* i_f_psamp_bits */
-};
-
-/*static void set_8bit_direct(i_img *im) {
-  im->i_f_ppix = i_ppix_d;
-  im->i_f_ppixf = i_ppixf_d;
-  im->i_f_plin = i_plin_d;
-  im->i_f_plinf = i_plinf_d;
-  im->i_f_gpix = i_gpix_d;
-  im->i_f_gpixf = i_gpixf_d;
-  im->i_f_glin = i_glin_d;
-  im->i_f_glinf = i_glinf_d;
-  im->i_f_gpal = NULL;
-  im->i_f_ppal = NULL;
-  im->i_f_addcolor = NULL;
-  im->i_f_getcolor = NULL;
-  im->i_f_colorcount = NULL;
-  im->i_f_findcolor = NULL;
-  }*/
-
-/*
-=item IIM_new(x, y, ch)
-
-=item i_img_8_new(x, y, ch)
-
-=category Image creation/destruction
-
-=synopsis i_img *img = i_img_8_new(width, height, channels);
-
-Creates a new image object I<x> pixels wide, and I<y> pixels high with
-I<ch> channels.
-
-=cut
-*/
-
-
-i_img *
-IIM_new(int x,int y,int ch) {
-  i_img *im;
-  mm_log((1,"IIM_new(x %d,y %d,ch %d)\n",x,y,ch));
-
-  im=i_img_empty_ch(NULL,x,y,ch);
-  
-  mm_log((1,"(%p) <- IIM_new\n",im));
-  return im;
-}
-
-
-void
-IIM_DESTROY(i_img *im) {
-  mm_log((1,"IIM_DESTROY(im* %p)\n",im));
-  i_img_destroy(im);
-  /*   myfree(cl); */
-}
-
-/* 
-=item i_img_new()
-
-Create new image reference - notice that this isn't an object yet and
-this should be fixed asap.
-
-=cut
-*/
-
-
-i_img *
-i_img_new() {
-  i_img *im;
-  
-  mm_log((1,"i_img_struct()\n"));
-
-  im = i_img_alloc();
-  
-  *im = IIM_base_8bit_direct;
-  im->xsize=0;
-  im->ysize=0;
-  im->channels=3;
-  im->ch_mask=MAXINT;
-  im->bytes=0;
-  im->idata=NULL;
-
-  i_img_init(im);
-  
-  mm_log((1,"(%p) <- i_img_struct\n",im));
-  return im;
-}
-
-/* 
-=item i_img_empty(im, x, y)
-
-Re-new image reference (assumes 3 channels)
-
-   im - Image pointer
-   x - xsize of destination image
-   y - ysize of destination image
-
-**FIXME** what happens if a live image is passed in here?
-
-Should this just call i_img_empty_ch()?
-
-=cut
-*/
-
-i_img *
-i_img_empty(i_img *im,int x,int y) {
-  mm_log((1,"i_img_empty(*im %p, x %d, y %d)\n",im, x, y));
-  return i_img_empty_ch(im, x, y, 3);
-}
-
-/* 
-=item i_img_empty_ch(im, x, y, ch)
-
-Re-new image reference 
-
-   im - Image pointer
-   x  - xsize of destination image
-   y  - ysize of destination image
-   ch - number of channels
-
-=cut
-*/
-
-i_img *
-i_img_empty_ch(i_img *im,int x,int y,int ch) {
-  int bytes;
-
-  mm_log((1,"i_img_empty_ch(*im %p, x %d, y %d, ch %d)\n", im, x, y, ch));
-
-  if (x < 1 || y < 1) {
-    i_push_error(0, "Image sizes must be positive");
-    return NULL;
-  }
-  if (ch < 1 || ch > MAXCHANNELS) {
-    i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS);
-    return NULL;
-  }
-  /* check this multiplication doesn't overflow */
-  bytes = x*y*ch;
-  if (bytes / y / ch != x) {
-    i_push_errorf(0, "integer overflow calculating image allocation");
-    return NULL;
-  }
-
-  if (im == NULL)
-    im = i_img_alloc();
-
-  memcpy(im, &IIM_base_8bit_direct, sizeof(i_img));
-  i_tags_new(&im->tags);
-  im->xsize    = x;
-  im->ysize    = y;
-  im->channels = ch;
-  im->ch_mask  = MAXINT;
-  im->bytes=bytes;
-  if ( (im->idata=mymalloc(im->bytes)) == NULL) 
-    i_fatal(2,"malloc() error\n"); 
-  memset(im->idata,0,(size_t)im->bytes);
-  
-  im->ext_data = NULL;
-
-  i_img_init(im);
-  
-  mm_log((1,"(%p) <- i_img_empty_ch\n",im));
-  return im;
-}
-
 /* 
 =item i_img_exorcise(im)
 
@@ -471,10 +253,6 @@ i_img_exorcise(i_img *im) {
   im->ysize    = 0;
   im->channels = 0;
 
-  im->i_f_ppix=i_ppix_d;
-  im->i_f_gpix=i_gpix_d;
-  im->i_f_plin=i_plin_d;
-  im->i_f_glin=i_glin_d;
   im->ext_data=NULL;
 }
 
@@ -1414,388 +1192,6 @@ i_get_anonymous_color_histo(i_img *im, unsigned int **col_usage, int maxc) {
 /*
 =back
 
-=head2 8-bit per sample image internal functions
-
-These are the functions installed in an 8-bit per sample image.
-
-=over
-
-=item i_ppix_d(im, x, y, col)
-
-Internal function.
-
-This is the function kept in the i_f_ppix member of an i_img object.
-It does a normal store of a pixel into the image with range checking.
-
-Returns 0 if the pixel could be set, -1 otherwise.
-
-=cut
-*/
-static
-int
-i_ppix_d(i_img *im, int x, int y, const i_color *val) {
-  int ch;
-  
-  if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
-    for(ch=0;ch<im->channels;ch++)
-      if (im->ch_mask&(1<<ch)) 
-	im->idata[(x+y*im->xsize)*im->channels+ch]=val->channel[ch];
-    return 0;
-  }
-  return -1; /* error was clipped */
-}
-
-/*
-=item i_gpix_d(im, x, y, &col)
-
-Internal function.
-
-This is the function kept in the i_f_gpix member of an i_img object.
-It does normal retrieval of a pixel from the image with range checking.
-
-Returns 0 if the pixel could be set, -1 otherwise.
-
-=cut
-*/
-static
-int 
-i_gpix_d(i_img *im, int x, int y, i_color *val) {
-  int ch;
-  if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
-    for(ch=0;ch<im->channels;ch++) 
-      val->channel[ch]=im->idata[(x+y*im->xsize)*im->channels+ch];
-    return 0;
-  }
-  for(ch=0;ch<im->channels;ch++) val->channel[ch] = 0;
-  return -1; /* error was cliped */
-}
-
-/*
-=item i_glin_d(im, l, r, y, vals)
-
-Reads a line of data from the image, storing the pixels at vals.
-
-The line runs from (l,y) inclusive to (r,y) non-inclusive
-
-vals should point at space for (r-l) pixels.
-
-l should never be less than zero (to avoid confusion about where to
-put the pixels in vals).
-
-Returns the number of pixels copied (eg. if r, l or y is out of range)
-
-=cut
-*/
-static
-int
-i_glin_d(i_img *im, int l, int r, int y, i_color *vals) {
-  int ch, count, i;
-  unsigned char *data;
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    count = r - l;
-    for (i = 0; i < count; ++i) {
-      for (ch = 0; ch < im->channels; ++ch)
-	vals[i].channel[ch] = *data++;
-    }
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=item i_plin_d(im, l, r, y, vals)
-
-Writes a line of data into the image, using the pixels at vals.
-
-The line runs from (l,y) inclusive to (r,y) non-inclusive
-
-vals should point at (r-l) pixels.
-
-l should never be less than zero (to avoid confusion about where to
-get the pixels in vals).
-
-Returns the number of pixels copied (eg. if r, l or y is out of range)
-
-=cut
-*/
-static
-int
-i_plin_d(i_img *im, int l, int r, int y, const i_color *vals) {
-  int ch, count, i;
-  unsigned char *data;
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    count = r - l;
-    for (i = 0; i < count; ++i) {
-      for (ch = 0; ch < im->channels; ++ch) {
-	if (im->ch_mask & (1 << ch)) 
-	  *data = vals[i].channel[ch];
-	++data;
-      }
-    }
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=item i_ppixf_d(im, x, y, val)
-
-=cut
-*/
-static
-int
-i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val) {
-  int ch;
-  
-  if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
-    for(ch=0;ch<im->channels;ch++)
-      if (im->ch_mask&(1<<ch)) {
-	im->idata[(x+y*im->xsize)*im->channels+ch] = 
-          SampleFTo8(val->channel[ch]);
-      }
-    return 0;
-  }
-  return -1; /* error was clipped */
-}
-
-/*
-=item i_gpixf_d(im, x, y, val)
-
-=cut
-*/
-static
-int
-i_gpixf_d(i_img *im, int x, int y, i_fcolor *val) {
-  int ch;
-  if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
-    for(ch=0;ch<im->channels;ch++) {
-      val->channel[ch] = 
-        Sample8ToF(im->idata[(x+y*im->xsize)*im->channels+ch]);
-    }
-    return 0;
-  }
-  return -1; /* error was cliped */
-}
-
-/*
-=item i_glinf_d(im, l, r, y, vals)
-
-Reads a line of data from the image, storing the pixels at vals.
-
-The line runs from (l,y) inclusive to (r,y) non-inclusive
-
-vals should point at space for (r-l) pixels.
-
-l should never be less than zero (to avoid confusion about where to
-put the pixels in vals).
-
-Returns the number of pixels copied (eg. if r, l or y is out of range)
-
-=cut
-*/
-static
-int
-i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals) {
-  int ch, count, i;
-  unsigned char *data;
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    count = r - l;
-    for (i = 0; i < count; ++i) {
-      for (ch = 0; ch < im->channels; ++ch)
-	vals[i].channel[ch] = Sample8ToF(*data++);
-    }
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=item i_plinf_d(im, l, r, y, vals)
-
-Writes a line of data into the image, using the pixels at vals.
-
-The line runs from (l,y) inclusive to (r,y) non-inclusive
-
-vals should point at (r-l) pixels.
-
-l should never be less than zero (to avoid confusion about where to
-get the pixels in vals).
-
-Returns the number of pixels copied (eg. if r, l or y is out of range)
-
-=cut
-*/
-static
-int
-i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals) {
-  int ch, count, i;
-  unsigned char *data;
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    count = r - l;
-    for (i = 0; i < count; ++i) {
-      for (ch = 0; ch < im->channels; ++ch) {
-	if (im->ch_mask & (1 << ch)) 
-	  *data = SampleFTo8(vals[i].channel[ch]);
-	++data;
-      }
-    }
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=item i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count)
-
-Reads sample values from im for the horizontal line (l, y) to (r-1,y)
-for the channels specified by chans, an array of int with chan_count
-elements.
-
-Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
-
-=cut
-*/
-static
-int
-i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, 
-              const int *chans, int chan_count) {
-  int ch, count, i, w;
-  unsigned char *data;
-
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    w = r - l;
-    count = 0;
-
-    if (chans) {
-      /* make sure we have good channel numbers */
-      for (ch = 0; ch < chan_count; ++ch) {
-        if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
-          return 0;
-        }
-      }
-      for (i = 0; i < w; ++i) {
-        for (ch = 0; ch < chan_count; ++ch) {
-          *samps++ = data[chans[ch]];
-          ++count;
-        }
-        data += im->channels;
-      }
-    }
-    else {
-      if (chan_count <= 0 || chan_count > im->channels) {
-	i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
-		      chan_count);
-	return 0;
-      }
-      for (i = 0; i < w; ++i) {
-        for (ch = 0; ch < chan_count; ++ch) {
-          *samps++ = data[ch];
-          ++count;
-        }
-        data += im->channels;
-      }
-    }
-
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=item i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count)
-
-Reads sample values from im for the horizontal line (l, y) to (r-1,y)
-for the channels specified by chan_mask, where bit 0 is the first
-channel.
-
-Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
-
-=cut
-*/
-static
-int
-i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, 
-           const int *chans, int chan_count) {
-  int ch, count, i, w;
-  unsigned char *data;
-  for (ch = 0; ch < chan_count; ++ch) {
-    if (chans[ch] < 0 || chans[ch] >= im->channels) {
-      i_push_errorf(0, "No channel %d in this image", chans[ch]);
-    }
-  }
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    w = r - l;
-    count = 0;
-
-    if (chans) {
-      /* make sure we have good channel numbers */
-      for (ch = 0; ch < chan_count; ++ch) {
-        if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
-          return 0;
-        }
-      }
-      for (i = 0; i < w; ++i) {
-        for (ch = 0; ch < chan_count; ++ch) {
-          *samps++ = Sample8ToF(data[chans[ch]]);
-          ++count;
-        }
-        data += im->channels;
-      }
-    }
-    else {
-      if (chan_count <= 0 || chan_count > im->channels) {
-	i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
-		      chan_count);
-	return 0;
-      }
-      for (i = 0; i < w; ++i) {
-        for (ch = 0; ch < chan_count; ++ch) {
-          *samps++ = Sample8ToF(data[ch]);
-          ++count;
-        }
-        data += im->channels;
-      }
-    }
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=back
-
 =head2 Image method wrappers
 
 These functions provide i_fsample_t functions in terms of their
@@ -326,6 +326,7 @@ extern i_img *i_img_masked_new(i_img *targ, i_img *mask, int x, int y,
 extern i_img *i_img_16_new(int x, int y, int ch);
 extern i_img *i_img_to_rgb16(i_img *im);
 extern i_img *i_img_double_new(int x, int y, int ch);
+extern i_img *i_img_to_drgb(i_img *im);
 
 extern int i_img_is_monochrome(i_img *im, int *zero_is_white);
 extern void i_get_file_background(i_img *im, i_color *bg);
@@ -114,4 +114,6 @@ i_img_dim i_maxx(i_img_dim x, i_img_dim y);
 #define i_min(a, b) i_minx((a), (b))
 #define i_max(a, b) i_maxx((a), (b))
 
+#define color_to_grey(col) ((col)->rgb.r * 0.222  + (col)->rgb.g * 0.707 + (col)->rgb.b * 0.071)
+
 #endif
@@ -17,8 +17,10 @@ extern im_ext_funcs *imager_function_ext_table;
     imager_function_ext_table = INT2PTR(im_ext_funcs *, SvIV(get_sv(PERL_FUNCTION_TABLE_NAME, 1))); \
     if (!imager_function_ext_table) \
       croak("Imager API function table not found!"); \
-    if (imager_function_ext_table->version != IMAGER_API_VERSION) \
-      croak("Imager API version incorrect"); \
+    if (imager_function_ext_table->version != IMAGER_API_VERSION) {  \
+      croak("Imager API version incorrect loaded %d vs expected %d", \
+	    imager_function_ext_table->version, IMAGER_API_VERSION); \
+    } \
     if (imager_function_ext_table->level < IMAGER_MIN_API_LEVEL) \
       croak("API level %d below minimum of %d", imager_function_ext_table->level, IMAGER_MIN_API_LEVEL); \
   } while (0)
@@ -192,7 +194,7 @@ extern im_ext_funcs *imager_function_ext_table;
 
 #define i_set_image_file_limits(max_width, max_height, max_bytes) \
   ((im_extt->f_i_set_image_file_limits)((max_width), (max_height), (max_bytes)))
-#define i_get_image_file_limits(max_width, max_height, max_bytes) \
+#define i_get_image_file_limits(pmax_width, pmax_height, pmax_bytes) \
   ((im_extt->f_i_get_image_file_limits)((pmax_width), (pmax_height), (pmax_bytes)))
 #define i_int_check_image_file_limits(width, height, channels, sample_size) \
   ((im_extt->f_i_int_check_image_file_limits)((width), (height), (channels), (sample_size)))
@@ -17,7 +17,7 @@ extern im_pl_ext_funcs *imager_perl_function_ext_table;
     imager_perl_function_ext_table = INT2PTR(im_pl_ext_funcs *, SvIV(get_sv(PERL_PL_FUNCTION_TABLE_NAME, 1))); \
     if (!imager_perl_function_ext_table) \
       croak("Imager Perl API function table not found!"); \
-    if (imager_perl_function_ext_table->version != IMAGER_API_VERSION) \
+    if (imager_perl_function_ext_table->version != IMAGER_PL_API_VERSION) \
       croak("Imager Perl API version incorrect"); \
     if (imager_perl_function_ext_table->level < IMAGER_MIN_PL_API_LEVEL) \
       croak("perl API level %d below minimum of %d", imager_perl_function_ext_table->level, IMAGER_MIN_PL_API_LEVEL); \
@@ -0,0 +1,616 @@
+#include "imager.h"
+#include "imageri.h"
+
+static int i_ppix_d(i_img *im, int x, int y, const i_color *val);
+static int i_gpix_d(i_img *im, int x, int y, i_color *val);
+static int i_glin_d(i_img *im, int l, int r, int y, i_color *vals);
+static int i_plin_d(i_img *im, int l, int r, int y, const i_color *vals);
+static int i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val);
+static int i_gpixf_d(i_img *im, int x, int y, i_fcolor *val);
+static int i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals);
+static int i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals);
+static int i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, const int *chans, int chan_count);
+static int i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, const int *chans, int chan_count);
+
+/*
+=item IIM_base_8bit_direct (static)
+
+A static i_img object used to initialize direct 8-bit per sample images.
+
+=cut
+*/
+static i_img IIM_base_8bit_direct =
+{
+  0, /* channels set */
+  0, 0, 0, /* xsize, ysize, bytes */
+  ~0U, /* ch_mask */
+  i_8_bits, /* bits */
+  i_direct_type, /* type */
+  0, /* virtual */
+  NULL, /* idata */
+  { 0, 0, NULL }, /* tags */
+  NULL, /* ext_data */
+
+  i_ppix_d, /* i_f_ppix */
+  i_ppixf_d, /* i_f_ppixf */
+  i_plin_d, /* i_f_plin */
+  i_plinf_d, /* i_f_plinf */
+  i_gpix_d, /* i_f_gpix */
+  i_gpixf_d, /* i_f_gpixf */
+  i_glin_d, /* i_f_glin */
+  i_glinf_d, /* i_f_glinf */
+  i_gsamp_d, /* i_f_gsamp */
+  i_gsampf_d, /* i_f_gsampf */
+
+  NULL, /* i_f_gpal */
+  NULL, /* i_f_ppal */
+  NULL, /* i_f_addcolors */
+  NULL, /* i_f_getcolors */
+  NULL, /* i_f_colorcount */
+  NULL, /* i_f_maxcolors */
+  NULL, /* i_f_findcolor */
+  NULL, /* i_f_setcolors */
+
+  NULL, /* i_f_destroy */
+
+  i_gsamp_bits_fb,
+  NULL, /* i_f_psamp_bits */
+};
+
+/*static void set_8bit_direct(i_img *im) {
+  im->i_f_ppix = i_ppix_d;
+  im->i_f_ppixf = i_ppixf_d;
+  im->i_f_plin = i_plin_d;
+  im->i_f_plinf = i_plinf_d;
+  im->i_f_gpix = i_gpix_d;
+  im->i_f_gpixf = i_gpixf_d;
+  im->i_f_glin = i_glin_d;
+  im->i_f_glinf = i_glinf_d;
+  im->i_f_gpal = NULL;
+  im->i_f_ppal = NULL;
+  im->i_f_addcolor = NULL;
+  im->i_f_getcolor = NULL;
+  im->i_f_colorcount = NULL;
+  im->i_f_findcolor = NULL;
+  }*/
+
+/*
+=item IIM_new(x, y, ch)
+
+=item i_img_8_new(x, y, ch)
+
+=category Image creation/destruction
+
+=synopsis i_img *img = i_img_8_new(width, height, channels);
+
+Creates a new image object I<x> pixels wide, and I<y> pixels high with
+I<ch> channels.
+
+=cut
+*/
+
+
+i_img *
+IIM_new(int x,int y,int ch) {
+  i_img *im;
+  mm_log((1,"IIM_new(x %d,y %d,ch %d)\n",x,y,ch));
+
+  im=i_img_empty_ch(NULL,x,y,ch);
+  
+  mm_log((1,"(%p) <- IIM_new\n",im));
+  return im;
+}
+
+
+void
+IIM_DESTROY(i_img *im) {
+  mm_log((1,"IIM_DESTROY(im* %p)\n",im));
+  i_img_destroy(im);
+  /*   myfree(cl); */
+}
+
+/* 
+=item i_img_new()
+
+Create new image reference - notice that this isn't an object yet and
+this should be fixed asap.
+
+=cut
+*/
+
+
+i_img *
+i_img_new() {
+  i_img *im;
+  
+  mm_log((1,"i_img_struct()\n"));
+
+  im = i_img_alloc();
+  
+  *im = IIM_base_8bit_direct;
+  im->xsize=0;
+  im->ysize=0;
+  im->channels=3;
+  im->ch_mask=MAXINT;
+  im->bytes=0;
+  im->idata=NULL;
+
+  i_img_init(im);
+  
+  mm_log((1,"(%p) <- i_img_struct\n",im));
+  return im;
+}
+
+/* 
+=item i_img_empty(im, x, y)
+
+Re-new image reference (assumes 3 channels)
+
+   im - Image pointer
+   x - xsize of destination image
+   y - ysize of destination image
+
+**FIXME** what happens if a live image is passed in here?
+
+Should this just call i_img_empty_ch()?
+
+=cut
+*/
+
+i_img *
+i_img_empty(i_img *im,int x,int y) {
+  mm_log((1,"i_img_empty(*im %p, x %d, y %d)\n",im, x, y));
+  return i_img_empty_ch(im, x, y, 3);
+}
+
+/* 
+=item i_img_empty_ch(im, x, y, ch)
+
+Re-new image reference 
+
+   im - Image pointer
+   x  - xsize of destination image
+   y  - ysize of destination image
+   ch - number of channels
+
+=cut
+*/
+
+i_img *
+i_img_empty_ch(i_img *im,int x,int y,int ch) {
+  int bytes;
+
+  mm_log((1,"i_img_empty_ch(*im %p, x %d, y %d, ch %d)\n", im, x, y, ch));
+
+  if (x < 1 || y < 1) {
+    i_push_error(0, "Image sizes must be positive");
+    return NULL;
+  }
+  if (ch < 1 || ch > MAXCHANNELS) {
+    i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS);
+    return NULL;
+  }
+  /* check this multiplication doesn't overflow */
+  bytes = x*y*ch;
+  if (bytes / y / ch != x) {
+    i_push_errorf(0, "integer overflow calculating image allocation");
+    return NULL;
+  }
+
+  if (im == NULL)
+    im = i_img_alloc();
+
+  memcpy(im, &IIM_base_8bit_direct, sizeof(i_img));
+  i_tags_new(&im->tags);
+  im->xsize    = x;
+  im->ysize    = y;
+  im->channels = ch;
+  im->ch_mask  = MAXINT;
+  im->bytes=bytes;
+  if ( (im->idata=mymalloc(im->bytes)) == NULL) 
+    i_fatal(2,"malloc() error\n"); 
+  memset(im->idata,0,(size_t)im->bytes);
+  
+  im->ext_data = NULL;
+
+  i_img_init(im);
+  
+  mm_log((1,"(%p) <- i_img_empty_ch\n",im));
+  return im;
+}
+
+/*
+=head2 8-bit per sample image internal functions
+
+These are the functions installed in an 8-bit per sample image.
+
+=over
+
+=item i_ppix_d(im, x, y, col)
+
+Internal function.
+
+This is the function kept in the i_f_ppix member of an i_img object.
+It does a normal store of a pixel into the image with range checking.
+
+Returns 0 if the pixel could be set, -1 otherwise.
+
+=cut
+*/
+static
+int
+i_ppix_d(i_img *im, int x, int y, const i_color *val) {
+  int ch;
+  
+  if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
+    for(ch=0;ch<im->channels;ch++)
+      if (im->ch_mask&(1<<ch)) 
+	im->idata[(x+y*im->xsize)*im->channels+ch]=val->channel[ch];
+    return 0;
+  }
+  return -1; /* error was clipped */
+}
+
+/*
+=item i_gpix_d(im, x, y, &col)
+
+Internal function.
+
+This is the function kept in the i_f_gpix member of an i_img object.
+It does normal retrieval of a pixel from the image with range checking.
+
+Returns 0 if the pixel could be set, -1 otherwise.
+
+=cut
+*/
+static
+int 
+i_gpix_d(i_img *im, int x, int y, i_color *val) {
+  int ch;
+  if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
+    for(ch=0;ch<im->channels;ch++) 
+      val->channel[ch]=im->idata[(x+y*im->xsize)*im->channels+ch];
+    return 0;
+  }
+  for(ch=0;ch<im->channels;ch++) val->channel[ch] = 0;
+  return -1; /* error was cliped */
+}
+
+/*
+=item i_glin_d(im, l, r, y, vals)
+
+Reads a line of data from the image, storing the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at space for (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+put the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+static
+int
+i_glin_d(i_img *im, int l, int r, int y, i_color *vals) {
+  int ch, count, i;
+  unsigned char *data;
+  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+    if (r > im->xsize)
+      r = im->xsize;
+    data = im->idata + (l+y*im->xsize) * im->channels;
+    count = r - l;
+    for (i = 0; i < count; ++i) {
+      for (ch = 0; ch < im->channels; ++ch)
+	vals[i].channel[ch] = *data++;
+    }
+    return count;
+  }
+  else {
+    return 0;
+  }
+}
+
+/*
+=item i_plin_d(im, l, r, y, vals)
+
+Writes a line of data into the image, using the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+get the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+static
+int
+i_plin_d(i_img *im, int l, int r, int y, const i_color *vals) {
+  int ch, count, i;
+  unsigned char *data;
+  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+    if (r > im->xsize)
+      r = im->xsize;
+    data = im->idata + (l+y*im->xsize) * im->channels;
+    count = r - l;
+    for (i = 0; i < count; ++i) {
+      for (ch = 0; ch < im->channels; ++ch) {
+	if (im->ch_mask & (1 << ch)) 
+	  *data = vals[i].channel[ch];
+	++data;
+      }
+    }
+    return count;
+  }
+  else {
+    return 0;
+  }
+}
+
+/*
+=item i_ppixf_d(im, x, y, val)
+
+=cut
+*/
+static
+int
+i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val) {
+  int ch;
+  
+  if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
+    for(ch=0;ch<im->channels;ch++)
+      if (im->ch_mask&(1<<ch)) {
+	im->idata[(x+y*im->xsize)*im->channels+ch] = 
+          SampleFTo8(val->channel[ch]);
+      }
+    return 0;
+  }
+  return -1; /* error was clipped */
+}
+
+/*
+=item i_gpixf_d(im, x, y, val)
+
+=cut
+*/
+static
+int
+i_gpixf_d(i_img *im, int x, int y, i_fcolor *val) {
+  int ch;
+  if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
+    for(ch=0;ch<im->channels;ch++) {
+      val->channel[ch] = 
+        Sample8ToF(im->idata[(x+y*im->xsize)*im->channels+ch]);
+    }
+    return 0;
+  }
+  return -1; /* error was cliped */
+}
+
+/*
+=item i_glinf_d(im, l, r, y, vals)
+
+Reads a line of data from the image, storing the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at space for (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+put the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+static
+int
+i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals) {
+  int ch, count, i;
+  unsigned char *data;
+  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+    if (r > im->xsize)
+      r = im->xsize;
+    data = im->idata + (l+y*im->xsize) * im->channels;
+    count = r - l;
+    for (i = 0; i < count; ++i) {
+      for (ch = 0; ch < im->channels; ++ch)
+	vals[i].channel[ch] = Sample8ToF(*data++);
+    }
+    return count;
+  }
+  else {
+    return 0;
+  }
+}
+
+/*
+=item i_plinf_d(im, l, r, y, vals)
+
+Writes a line of data into the image, using the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+get the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+static
+int
+i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals) {
+  int ch, count, i;
+  unsigned char *data;
+  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+    if (r > im->xsize)
+      r = im->xsize;
+    data = im->idata + (l+y*im->xsize) * im->channels;
+    count = r - l;
+    for (i = 0; i < count; ++i) {
+      for (ch = 0; ch < im->channels; ++ch) {
+	if (im->ch_mask & (1 << ch)) 
+	  *data = SampleFTo8(vals[i].channel[ch]);
+	++data;
+      }
+    }
+    return count;
+  }
+  else {
+    return 0;
+  }
+}
+
+/*
+=item i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count)
+
+Reads sample values from im for the horizontal line (l, y) to (r-1,y)
+for the channels specified by chans, an array of int with chan_count
+elements.
+
+Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
+
+=cut
+*/
+static
+int
+i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, 
+              const int *chans, int chan_count) {
+  int ch, count, i, w;
+  unsigned char *data;
+
+  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+    if (r > im->xsize)
+      r = im->xsize;
+    data = im->idata + (l+y*im->xsize) * im->channels;
+    w = r - l;
+    count = 0;
+
+    if (chans) {
+      /* make sure we have good channel numbers */
+      for (ch = 0; ch < chan_count; ++ch) {
+        if (chans[ch] < 0 || chans[ch] >= im->channels) {
+          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+          return 0;
+        }
+      }
+      for (i = 0; i < w; ++i) {
+        for (ch = 0; ch < chan_count; ++ch) {
+          *samps++ = data[chans[ch]];
+          ++count;
+        }
+        data += im->channels;
+      }
+    }
+    else {
+      if (chan_count <= 0 || chan_count > im->channels) {
+	i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
+		      chan_count);
+	return 0;
+      }
+      for (i = 0; i < w; ++i) {
+        for (ch = 0; ch < chan_count; ++ch) {
+          *samps++ = data[ch];
+          ++count;
+        }
+        data += im->channels;
+      }
+    }
+
+    return count;
+  }
+  else {
+    return 0;
+  }
+}
+
+/*
+=item i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count)
+
+Reads sample values from im for the horizontal line (l, y) to (r-1,y)
+for the channels specified by chan_mask, where bit 0 is the first
+channel.
+
+Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
+
+=cut
+*/
+static
+int
+i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, 
+           const int *chans, int chan_count) {
+  int ch, count, i, w;
+  unsigned char *data;
+  for (ch = 0; ch < chan_count; ++ch) {
+    if (chans[ch] < 0 || chans[ch] >= im->channels) {
+      i_push_errorf(0, "No channel %d in this image", chans[ch]);
+    }
+  }
+  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+    if (r > im->xsize)
+      r = im->xsize;
+    data = im->idata + (l+y*im->xsize) * im->channels;
+    w = r - l;
+    count = 0;
+
+    if (chans) {
+      /* make sure we have good channel numbers */
+      for (ch = 0; ch < chan_count; ++ch) {
+        if (chans[ch] < 0 || chans[ch] >= im->channels) {
+          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+          return 0;
+        }
+      }
+      for (i = 0; i < w; ++i) {
+        for (ch = 0; ch < chan_count; ++ch) {
+          *samps++ = Sample8ToF(data[chans[ch]]);
+          ++count;
+        }
+        data += im->channels;
+      }
+    }
+    else {
+      if (chan_count <= 0 || chan_count > im->channels) {
+	i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
+		      chan_count);
+	return 0;
+      }
+      for (i = 0; i < w; ++i) {
+        for (ch = 0; ch < chan_count; ++ch) {
+          *samps++ = Sample8ToF(data[ch]);
+          ++count;
+        }
+        data += im->channels;
+      }
+    }
+    return count;
+  }
+  else {
+    return 0;
+  }
+}
+
+/*
+=back
+
+=head1 AUTHOR
+
+Arnar M. Hrafnkelsson <addi@umich.edu>
+
+Tony Cook <tony@develop-help.com>
+
+=head1 SEE ALSO
+
+L<Imager>
+
+=cut
+*/
@@ -399,6 +399,37 @@ static int i_gsampf_ddoub(i_img *im, int l, int r, int y, i_fsample_t *samps,
   }
 }
 
+/*
+=item i_img_to_drgb(im)
+
+=category Image creation
+
+Returns a double/sample version of the supplied image.
+
+Returns the image on success, or NULL on failure.
+
+=cut
+*/
+
+i_img *
+i_img_to_drgb(i_img *im) {
+  i_img *targ;
+  i_fcolor *line;
+  int y;
+
+  targ = i_img_double_new(im->xsize, im->ysize, im->channels);
+  if (!targ)
+    return NULL;
+  line = mymalloc(sizeof(i_fcolor) * im->xsize);
+  for (y = 0; y < im->ysize; ++y) {
+    i_glinf(im, 0, im->xsize, y, line);
+    i_plinf(targ, 0, im->xsize, y, line);
+  }
+
+  myfree(line);
+
+  return targ;
+}
 
 /*
 =back
@@ -252,7 +252,7 @@ should include Imager in your configure_requires:
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -1003,7 +1003,7 @@ I<ch> channels.
 
 
 =for comment
-From: File image.c
+From: File img8.c
 
 =item i_img_double_new(int x, int y, int ch)
 
@@ -1618,7 +1618,7 @@ From: File io.c
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -1186,7 +1186,7 @@ unified yet.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>, Arnar M. Hrafnkelsson.
+Tony Cook <tonyc@cpan.org>, Arnar M. Hrafnkelsson.
 
 =head1 SEE ALSO
 
@@ -122,7 +122,7 @@ __END__
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION
 
@@ -496,26 +496,32 @@ to control output:
 
 =over
 
-=item C<jpeg_density_unit>
+=item *
 
-The value of the density unit field in the C<JFIF> header.  This is
-ignored on writing if the C<i_aspect_only> tag is non-zero.
+C<jpeg_density_unit> - The value of the density unit field in the
+C<JFIF> header.  This is ignored on writing if the C<i_aspect_only>
+tag is non-zero.
 
 The C<i_xres> and C<i_yres> tags are expressed in pixels per inch no
 matter the value of this tag, they will be converted to/from the value
 stored in the JPEG file.
 
-=item C<jpeg_density_unit_name>
+=item *
 
-This is set when reading a JPEG file to the name of the unit given by
-C<jpeg_density_unit>.  Possible results include C<inch>,
-C<centimeter>, C<none> (the C<i_aspect_only> tag is also set reading
-these files).  If the value of C<jpeg_density_unit> is unknown then
-this tag isn't set.
+C<jpeg_density_unit_name> - This is set when reading a JPEG file to
+the name of the unit given by C<jpeg_density_unit>.  Possible results
+include C<inch>, C<centimeter>, C<none> (the C<i_aspect_only> tag is
+also set reading these files).  If the value of C<jpeg_density_unit>
+is unknown then this tag isn't set.
 
-=item C<jpeg_comment>
+=item *
+
+C<jpeg_comment> - Text comment.
+
+=item *
 
-Text comment.
+C<jpeg_progressive> - Whether the JPEG file is a progressive
+file. (Imager 0.84)
 
 =back
 
@@ -748,7 +748,7 @@ For large sample images this is scaled down to the range 0 .. 1.
 
 =head1 AUTHOR
 
-Arnar M. Hrafnkelsson, Tony Cook <tony@imager.perl.org>.
+Arnar M. Hrafnkelsson, Tony Cook <tonyc@cpan.org>.
 
 =head1 SEE ALSO
 
@@ -51,7 +51,7 @@ description directly to the drawing method.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =cut
 
@@ -99,7 +99,7 @@ Returns 0 on success, -1 on failure.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -638,13 +638,21 @@ No parameters.
 
 =item to_rgb16()
 
-You can convert a paletted image (or any image) to an 16-bit/channel
-RGB image with:
+Convert a paletted image (or any image) to a 16-bit/channel RGB image.
 
   $rgbimg = $img->to_rgb16;
 
 No parameters.
 
+=item to_rgb_double()
+
+Convert a paletted image (or any image) to an double/channel direct
+color image.
+
+  $rgbimg = $img->to_rgb_double;
+
+No parameters.
+
 =item masked()
 
 Creates a masked image.  A masked image lets you create an image proxy
@@ -1039,7 +1047,8 @@ Possible values are:
 
 =item *
 
-C<giflib> - the C<giflib> native quantization function is used.
+C<giflib> - this is a historical equivalent for C<closest> that also
+forces C<make_colors> to C<mediancut>.
 
 =item *
 
@@ -1052,7 +1061,9 @@ closest color is chosen.
 
 =item *
 
-C<errdiff> - an error diffusion dither is performed.
+C<errdiff> - an error diffusion dither is performed.  If the supplied
+(or generated) palette contains only grays the source colors are
+converted to gray before error diffusion is performed.
 
 =back
 
@@ -1120,9 +1131,10 @@ This function is a mess, it can take the following named parameters:
 
 =item *
 
-C<log> - name of a log file to log Imager's actions to.  Not all actions
-are logged, but the debugging memory allocator does log allocations
-here.  Ignored if Imager has been built without logging support.
+C<log> - name of a log file to log Imager's actions to.  Not all
+actions are logged, but the debugging memory allocator does log
+allocations here.  Ignored if Imager has been built without logging
+support.  Preferably use the open_log() method instead.
 
 =item *
 
@@ -1147,6 +1159,69 @@ Example:
 
 =back
 
+=head1 LOGGING METHODS
+
+Imager can open an internal log to send debugging information to.
+This log is extensively used in Imager's tests, but you're unlikely to
+use it otherwise.
+
+If Imager has been built with logging disabled, the methods fail
+quietly.
+
+=over
+
+=item open_log()
+
+Open the Imager debugging log file.
+
+=over
+
+=item *
+
+C<log> - the file name to log to.  If this is undef logging information
+is sent to the standard error stream.
+
+=item *
+
+C<loglevel> the level of logging to produce.  Default: 1.
+
+=back
+
+Returns a true value if the log file was opened successfully.
+
+  # send debug output to test.log
+  Imager->open_log(log => "test.log");
+
+  # send debug output to stderr
+  Imager->open_log();
+
+=item close_log()
+
+Close the Imager debugging log file and disable debug logging.
+
+No parameters.
+
+  Imager->close_log();
+
+=item log()
+
+ Imager->log($message)
+ Imager->log($message, $level)
+
+This method does not use named parameters.
+
+The default for C<$level> is 1.
+
+Send a message to the debug log.
+
+  Imager->log("My code got here!");
+
+=item is_logging()
+
+Returns a true value if logging is enabled.
+
+=back
+
 =head1 REVISION
 
 $Revision$
@@ -62,7 +62,7 @@ C<"Imager:" ":Color">.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION
 
@@ -107,7 +107,7 @@ of the image, using filled => 1 and color works at 8-bits/sample
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =cut
 
@@ -319,6 +319,6 @@ Other types, functions and values may be added in the future.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =cut
@@ -963,7 +963,7 @@ L<Imager>, L<Imager::Engines>
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>, Arnar M. Hrafnkelsson
+Tony Cook <tonyc@cpan.org>, Arnar M. Hrafnkelsson
 
 =head1 REVISION
 
@@ -173,7 +173,7 @@ so the complete program is:
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION
 
@@ -1,6 +1,9 @@
 #include "imconfig.h"
 #include "log.h"
 #include <stdlib.h>
+#include <errno.h>
+
+#ifdef IMAGER_LOG
 
 #define DTBUFF 50
 #define DATABUFF DTBUFF+3+10+1+5+1+1
@@ -12,14 +15,13 @@ static char  date_buffer[DTBUFF];
 static char  data_buffer[DATABUFF];
 
 
-#ifdef IMAGER_LOG
-
 /*
  * Logging is active
  */
 
-void
+int
 i_init_log(const char* name,int level) {
+  i_clear_error();
   log_level = level;
   if (level < 0) {
     lg_file = NULL;
@@ -28,13 +30,17 @@ i_init_log(const char* name,int level) {
       lg_file = stderr;
     } else {
       if (NULL == (lg_file = fopen(name, "w+")) ) { 
-	fprintf(stderr,"Cannot open file '%s'\n",name);
-	exit(2);
+	i_push_errorf(errno, "Cannot open file '%s': (%d)", name, errno);
+	return 0;
       }
     }
   }
-  setvbuf(lg_file, NULL, _IONBF, BUFSIZ);
-  mm_log((0,"Imager - log started (level = %d)\n", level));
+  if (lg_file) {
+    setvbuf(lg_file, NULL, _IONBF, BUFSIZ);
+    mm_log((0,"Imager - log started (level = %d)\n", level));
+  }
+
+  return lg_file != NULL;
 }
 
 void
@@ -55,17 +61,6 @@ i_fatal(int exitcode,const char *fmt, ... ) {
   exit(exitcode);
 }
 
-#else
-
-/*
- * Logging is inactive - insert dummy functions
- */
-
-void i_init_log(const char* name,int onoff) {}
-void i_fatal(int exitcode,const char *fmt, ... ) { exit(exitcode); }
-
-
-#endif
 
 /*
 =item i_loog(level, format, ...)
@@ -111,3 +106,26 @@ i_lhead(const char *file, int line) {
     sprintf(data_buffer, "[%s] %10s:%-5d ", date_buffer, file, line);
   }
 }
+
+#else
+
+/*
+ * Logging is inactive - insert dummy functions
+ */
+
+int i_init_log(const char* name,int onoff) {
+  i_clear_error();
+  i_push_error(0, "Logging disabled");
+  return 0;
+}
+
+void i_fatal(int exitcode,const char *fmt, ... ) { exit(exitcode); }
+
+void
+i_loog(int level,const char *fmt, ... ) {
+}
+
+void
+i_lhead(const char *file, int line) { }
+
+#endif
@@ -10,10 +10,10 @@
    global: creates a global variable FILE* lg_file
 */
 
+int i_init_log( const char *name, int onoff );
+void i_fatal ( int exitcode,const char *fmt, ... );
 void i_lhead ( const char *file, int line  );
-void i_init_log( const char *name, int onoff );
 void i_loog(int level,const char *msg, ... );
-void i_fatal ( int exitcode,const char *fmt, ... );
 
 /*
 =item mm_log((level, format, ...))
@@ -1,4 +1,5 @@
 #include "imager.h"
+#include "imageri.h"
 
 /*
 =item i_copyto(C<dest>, C<src>, C<x1>, C<y1>, C<x2>, C<y2>, C<tx>, C<ty>)
@@ -59,8 +60,6 @@ i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty)
 #/code
 }
 
-#define color_to_grey(col) ((col)->rgb.r * 0.222  + (col)->rgb.g * 0.707 + (col)->rgb.b * 0.071)
-
 #code
 void
 #ifdef IM_EIGHT_BIT
@@ -955,7 +955,7 @@ i_writeppm_wiol(i_img *im, io_glue *ig) {
 
 =head1 AUTHOR
 
-Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook<tony@imager.perl.org>,
+Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook <tonyc@cpan.org>,
 Philip Gwyn <gwyn@cpan.org>.
 
 =head1 SEE ALSO
@@ -5,10 +5,13 @@
 #include "imager.h"
 #include "imageri.h"
 
+static void makemap_webmap(i_quantize *);
 static void makemap_addi(i_quantize *, i_img **imgs, int count);
 static void makemap_mediancut(i_quantize *, i_img **imgs, int count);
 static void makemap_mono(i_quantize *);
 
+static int makemap_palette(i_quantize *, i_img **imgs, int count);
+
 static
 void
 setcol(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
@@ -58,15 +61,7 @@ i_quant_makemap(i_quantize *quant, i_img **imgs, int count) {
     /* use user's specified map */
     break;
   case mc_web_map:
-    {
-      int r, g, b;
-      int i = 0;
-      for (r = 0; r < 256; r+=0x33)
-	for (g = 0; g < 256; g+=0x33)
-	  for (b = 0; b < 256; b += 0x33)
-	    setcol(quant->mc_colors+i++, r, g, b, 255);
-      quant->mc_count = i;
-    }
+    makemap_webmap(quant);
     break;
 
   case mc_median_cut:
@@ -223,9 +218,12 @@ eucl_d_ch(cvec* cv,i_sample_t *chans) {
     + PWR2(cv->b - chans[2]);
 }
 
-static
-int
-ceucl_d(i_color *c1, i_color *c2) { return PWR2(c1->channel[0]-c2->channel[0])+PWR2(c1->channel[1]-c2->channel[1])+PWR2(c1->channel[2]-c2->channel[2]); }
+static int
+ceucl_d(i_color *c1, i_color *c2) {
+return PWR2(c1->channel[0]-c2->channel[0])
+  +PWR2(c1->channel[1]-c2->channel[1])
+  +PWR2(c1->channel[2]-c2->channel[2]);
+}
 
 static const int
 gray_samples[] = { 0, 0, 0 };
@@ -296,6 +294,9 @@ makemap_addi(i_quantize *quant, i_img **imgs, int count) {
 
   mm_log((1, "makemap_addi(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n", 
           quant, quant->mc_count, quant->mc_colors, imgs, count));
+
+  if (makemap_palette(quant, imgs, count))
+    return;
          
   i_mempool_init(&mp);
 
@@ -443,6 +444,8 @@ makemap_addi(i_quantize *quant, i_img **imgs, int count) {
 #endif
 
   i_mempool_destroy(&mp);
+
+  mm_log((1, "makemap_addi() - %d colors\n", quant->mc_count));
 }
 
 typedef struct {
@@ -546,7 +549,11 @@ makemap_mediancut(i_quantize *quant, i_img **imgs, int count) {
      this isn't terribly efficient, but it should work */
   int chan_count; 
 
-  /*printf("images %d  pal size %d\n", count, quant->mc_size);*/
+  mm_log((1, "makemap_mediancut(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n", 
+          quant, quant->mc_count, quant->mc_colors, imgs, count));
+
+  if (makemap_palette(quant, imgs, count))
+    return;
 
   i_mempool_init(&mp);
 
@@ -701,6 +708,8 @@ makemap_mediancut(i_quantize *quant, i_img **imgs, int count) {
   }
   /*printf("out %d colors\n", quant->mc_count);*/
   i_mempool_destroy(&mp);
+
+  mm_log((1, "makemap_mediancut() - %d colors\n", quant->mc_count));
 }
 
 static void
@@ -716,6 +725,112 @@ makemap_mono(i_quantize *quant) {
   quant->mc_count = 2;
 }
 
+static void
+makemap_webmap(i_quantize *quant) {
+  int r, g, b;
+
+  int i = 0;
+  for (r = 0; r < 256; r+=0x33)
+    for (g = 0; g < 256; g+=0x33)
+      for (b = 0; b < 256; b += 0x33)
+	setcol(quant->mc_colors+i++, r, g, b, 255);
+  quant->mc_count = i;
+}
+
+static int 
+in_palette(i_color *c, i_quantize *quant, int size) {
+  int i;
+
+  for (i = 0; i < size; ++i) {
+    if (c->channel[0] == quant->mc_colors[i].channel[0]
+        && c->channel[1] == quant->mc_colors[i].channel[1]
+        && c->channel[2] == quant->mc_colors[i].channel[2]) {
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+/*
+=item makemap_palette(quant, imgs, count)
+
+Tests if all the given images are paletted and have a common palette,
+if they do it builds that palette.
+
+A possible improvement might be to eliminate unused colors in the
+images palettes.
+
+=cut
+*/
+
+static int
+makemap_palette(i_quantize *quant, i_img **imgs, int count) {
+  int size = quant->mc_count;
+  int i;
+  int imgn;
+  char used[256];
+  int col_count;
+
+  mm_log((1, "makemap_palette(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n", 
+          quant, quant->mc_count, quant->mc_colors, imgs, count));
+  /* we try to build a common palette here, if we can manage that, then
+     that's the palette we use */
+  for (imgn = 0; imgn < count; ++imgn) {
+    int eliminate_unused;
+    if (imgs[imgn]->type != i_palette_type) {
+      mm_log((1, "makemap_palette() -> 0 (non-palette image)\n"));
+      return 0;
+    }
+
+    if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0, 
+                        &eliminate_unused)) {
+      eliminate_unused = 1;
+    }
+
+    if (eliminate_unused) {
+      i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
+      int x, y;
+      memset(used, 0, sizeof(used));
+
+      for (y = 0; y < imgs[imgn]->ysize; ++y) {
+        i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
+        for (x = 0; x < imgs[imgn]->xsize; ++x)
+          used[line[x]] = 1;
+      }
+
+      myfree(line);
+    }
+    else {
+      /* assume all are in use */
+      memset(used, 1, sizeof(used));
+    }
+
+    col_count = i_colorcount(imgs[imgn]);
+    for (i = 0; i < col_count; ++i) {
+      i_color c;
+      
+      i_getcolors(imgs[imgn], i, &c, 1);
+      if (used[i]) {
+        if (in_palette(&c, quant, size) < 0) {
+          if (size < quant->mc_size) {
+            quant->mc_colors[size++] = c;
+          }
+          else {
+	    mm_log((1, "makemap_palette() -> 0 (too many colors)\n"));
+            return 0;
+          }
+        }
+      }
+    }
+  }
+
+  mm_log((1, "makemap_palette() -> 1 (%d total colors)\n", size));
+  quant->mc_count = size;
+
+  return 1;
+}
+
 #define pboxjump 32
 
 /* Define one of the following 4 symbols to choose a colour search method
@@ -755,6 +870,23 @@ makemap_mono(i_quantize *quant) {
 /*#define IM_CFRAND2DIST*/
 #endif
 
+/* return true if the color map contains only grays */
+static int
+is_gray_map(const i_quantize *quant) {
+  int i;
+
+  for (i = 0; i < quant->mc_count; ++i) {
+    if (quant->mc_colors[i].rgb.r != quant->mc_colors[i].rgb.g
+	|| quant->mc_colors[i].rgb.r != quant->mc_colors[i].rgb.b) {
+      mm_log((1, "  not a gray map\n"));
+      return 0;
+    }
+  }
+
+  mm_log((1, "  is a gray map\n"));
+  return 1;
+}
+
 #ifdef IM_CFHASHBOX
 
 /* The original version I wrote for this used the sort.
@@ -1205,6 +1337,7 @@ translate_errdiff(i_quantize *quant, i_img *img, i_palidx *out) {
   int difftotal;
   int x, y, dx, dy;
   int bst_idx = 0;
+  int is_gray = is_gray_map(quant);
   CF_VARS;
 
   if ((quant->errdiff & ed_mask) == ed_custom) {
@@ -1249,6 +1382,10 @@ translate_errdiff(i_quantize *quant, i_img *img, i_palidx *out) {
       if (img->channels < 3) {
         val.channel[1] = val.channel[2] = val.channel[0];
       }
+      else if (is_gray) {
+	int gray = 0.5 + color_to_grey(&val);
+	val.channel[0] = val.channel[1] = val.channel[2] = gray;
+      }
       perr = err[x+mapo];
       perr.r = perr.r < 0 ? -((-perr.r)/difftotal) : perr.r/difftotal;
       perr.g = perr.g < 0 ? -((-perr.g)/difftotal) : perr.g/difftotal;
@@ -81,7 +81,7 @@ of the effect of the different alignments.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
@@ -145,7 +145,7 @@ Using JPEG as the output format is not recommended.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =for stopwords Oppenheim
 
@@ -89,6 +89,6 @@ scale factor.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =cut
@@ -86,7 +86,7 @@ http://www.perlmonks.org/?node_id=539316
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION
 
@@ -122,7 +122,7 @@ might be useful.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =for stopwords Oppenheim
 
@@ -78,7 +78,7 @@ cautions and explanations.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION
 
@@ -68,7 +68,7 @@ cautions and explanations.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION
 
@@ -205,7 +205,7 @@ cases the actual text will not be touching these boundaries.
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION
 
@@ -64,6 +64,6 @@ $Revision$
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =cut
@@ -3,7 +3,7 @@
 # to make sure we get expected values
 
 use strict;
-use Test::More tests => 230;
+use Test::More tests => 233;
 
 BEGIN { use_ok(Imager => qw(:handy :all)) }
 
@@ -11,7 +11,7 @@ use Imager::Test qw(image_bounds_checks is_color3 is_color4 is_fcolor4 color_cmp
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t01introvert.log",1);
+Imager->open_log(log => "testout/t01introvert.log");
 
 my $im_g = Imager::ImgRaw::new(100, 101, 1);
 
@@ -132,7 +132,8 @@ is(Imager::i_img_type($im_pal), 0, "pal img shouldn't be paletted now");
 
 # test the OO interfaces
 my $impal2 = Imager->new(type=>'pseudo', xsize=>200, ysize=>201);
-ok($impal2, "make paletted via OO");
+ok($impal2, "make paletted via OO")
+  or diag(Imager->errstr);
 is($impal2->getchannels, 3, "check channels");
 is($impal2->bits, 8, "check bits");
 is($impal2->type, 'paletted', "check type");
@@ -167,7 +168,8 @@ is($impal2->getheight, 201, "check height");
      "we can setcolors");
 
   # make an rgb version
-  my $imrgb2 = $impal2->to_rgb8();
+  my $imrgb2 = $impal2->to_rgb8()
+    or diag($impal2->errstr);
   is($imrgb2->type, 'direct', "converted is direct");
 
   # and back again, specifying the palette
@@ -183,6 +185,13 @@ is($impal2->getheight, 201, "check height");
   is($impal3->type, 'paletted', "and is paletted");
 }
 
+{ # to_rgb on incomplete image
+  my $im = Imager->new;
+  ok($im, "make empty image");
+  ok(!$im->to_rgb8, "convert to rgb8");
+  is($im->errstr, "empty input image", "check message");
+}
+
 { # basic checks, 8-bit direct images
   my $im = Imager->new(xsize => 2, ysize => 3);
   ok($im, 'create 8-bit direct image');
@@ -542,6 +551,12 @@ cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
   image_bounds_checks($im);
 }
 
+Imager->close_log();
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t01introvert.log";
+}
+
 sub check_add {
   my ($im, $color, $expected) = @_;
   my $index = Imager::i_addcolors($im, $color);
@@ -6,7 +6,7 @@ use Imager::Test qw(is_color3 is_fcolor3);
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t020masked.log", 1);
+Imager->open_log(log => "testout/t020masked.log");
 
 my $base_rgb = Imager::ImgRaw::new(100, 100, 3);
 # put something in there
@@ -529,3 +529,9 @@ $mask->box(fill => { hatch => "check1x1" }, ymin => 40, xmax => 39);
        2, "write over right side");
   }
 }
+
+Imager->close_log();
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t020masked.log";
+}
@@ -1,12 +1,12 @@
 #!perl -w
 use strict;
-use Test::More tests => 104;
+use Test::More tests => 107;
 
 BEGIN { use_ok(Imager=>qw(:all :handy)) }
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t021sixteen.log", 1);
+Imager->open_log(log => "testout/t021sixteen.log");
 
 use Imager::Color::Float;
 use Imager::Test qw(test_image is_image image_bounds_checks test_colorf_gpix
@@ -206,6 +206,13 @@ cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
   is_image($im, $im16, "check image data matches");
 }
 
+{ # empty image handling
+  my $im = Imager->new;
+  ok($im, "make empty image");
+  ok(!$im->to_rgb16, "convert empty image to 16-bit");
+  is($im->errstr, "empty input image", "check message");
+}
+
 { # bounds checks
   my $im = Imager->new(xsize => 10, ysize => 10, bits => 16);
   image_bounds_checks($im);
@@ -223,3 +230,9 @@ cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
     or print "# ", $im->errstr, "\n";
   is_deeply(\@wr_samples, \@samples, "check it matches");
 }
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t021sixteen.log";
+}
@@ -1,12 +1,13 @@
 #!perl -w
 use strict;
-use Test::More tests => 83;
-
+use Test::More tests => 88;
 BEGIN { use_ok(Imager => qw(:all :handy)) }
 
+use Imager::Test qw(test_image is_image);
+
 -d "testout" or mkdir "testout";
 
-init_log("testout/t022double.log", 1);
+Imager->open_log(log => "testout/t022double.log");
 
 use Imager::Test qw(image_bounds_checks test_colorf_gpix test_colorf_glin mask_tests);
 
@@ -151,3 +152,25 @@ cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
   my $im = Imager->new(xsize => 10, ysize=>10, bits=>'double');
   image_bounds_checks($im);
 }
+
+
+{ # convert to rgb double
+  my $im = test_image();
+  my $imdb = $im->to_rgb_double;
+  print "# check conversion to double\n";
+  is($imdb->bits, "double", "check bits");
+  is_image($im, $imdb, "check image data matches");
+}
+
+{ # empty image handling
+  my $im = Imager->new;
+  ok($im, "make empty image");
+  ok(!$im->to_rgb_double, "convert empty image to double");
+  is($im->errstr, "empty input image", "check message");
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t022double.log";
+}
@@ -1,10 +1,12 @@
 #!perl -w
 # some of this is tested in t01introvert.t too
 use strict;
-use Test::More tests => 126;
+use Test::More tests => 128;
 BEGIN { use_ok("Imager"); }
 
-use Imager::Test qw(image_bounds_checks test_image is_color3);
+use Imager::Test qw(image_bounds_checks test_image is_color3 isnt_image);
+
+Imager->open_log(log => "testout/t023palette.log");
 
 sub isbin($$$);
 
@@ -343,6 +345,21 @@ cmp_ok(Imager->errstr, '=~', qr/Channels must be positive and <= 4/,
   is_color3($colors[8], 0, 0x33, 0x66, "9th should be 003366");
 }
 
+{ # RT 68508
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  $im->box(filled => 1, color => Imager::Color->new(255, 0, 0));
+  my $palim = $im->to_paletted(make_colors => "mono", translate => "errdiff");
+  ok($palim, "convert to mono with error diffusion");
+  my $blank = Imager->new(xsize => 10, ysize => 10);
+  isnt_image($palim, $blank, "make sure paletted isn't all black");
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t023palette.log"
+}
+
 sub iscolor {
   my ($c1, $c2, $msg) = @_;
 
@@ -1,12 +1,11 @@
-BEGIN { $|=1; print "1..2\n"; }
-END { print "not ok 1\n" unless $loaded; };
-use Imager qw(:all);
-++$loaded;
-print "ok 1\n";
+#!perl -w
+use strict;
+use Test::More tests => 7;
+BEGIN { use_ok("Imager", ":all") }
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t05error.log", 1);
+Imager->open_log(log => "testout/t05error.log");
 
 # try to read an invalid pnm file
 open FH, "< testimg/junk.ppm"
@@ -14,34 +13,31 @@ open FH, "< testimg/junk.ppm"
 binmode(FH);
 my $IO = Imager::io_new_fd(fileno(FH));
 my $im = i_readpnm_wiol($IO, -1);
-if ($im) {
-  print "not ok 2 # read of junk.ppm should have failed\n";
-}
-else {
+SKIP:{
+  ok(!$im, "read of junk.ppm should have failed")
+    or skip("read didn't fail!", 5);
+
   my @errors = Imager::i_errors();
-  unless (@errors) {
-    print "not ok 2 # no errors from i_errors()\n";
-  }
-  else {
-    # each entry must be an array ref with 2 elements
-    my $bad;
-    for (@errors) {
-      $bad = 1;
-      if (ref ne 'ARRAY') {
-	print "not ok 2 # element not an array ref\n";
-	last;
-      }
-      if (@$_ != 2) {
-	print "not ok 2 # elements array didn't have 2 elements\n";
-	last;
-      }
-      $bad = 0;
-    }
-    unless ($bad) {
-      print "ok 2\n";
-      for (@errors) {
-	print "# $_->[0]/$_->[1]\n";
-      }
-    }
+
+  is(scalar @errors, 1, "got the errors")
+    or skip("no errors to check", 4);
+
+ SKIP:
+  {
+    my $error0 = $errors[0];
+    is(ref $error0, "ARRAY", "entry 0 is an array ref")
+      or skip("entry 0 not an array", 3);
+
+    is(scalar @$error0, 2, "entry 0 has 2 elements")
+      or skip("entry 0 doesn't have enough elements", 2);
+
+    is($error0->[0], "while skipping to height", "check message");
+    is($error0->[1], "0", "error code should be 0");
   }
 }
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t05error.log";
+}
@@ -8,7 +8,7 @@ BEGIN { use_ok(Imager => ':all') };
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t07iolayer.log", 1);
+Imager->open_log(log => "testout/t07iolayer.log");
 
 undef($/);
 # start by testing io buffer
@@ -252,6 +252,12 @@ SKIP:
   is($io->close, 0, "close");
 }
 
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t07.ppm", "testout/t07iolayer.log";
+}
+
 sub eof_read {
   my ($max_len) = @_;
 
@@ -9,7 +9,7 @@ use Imager;
 
 -d "testout" or mkdir "testout";
 
-Imager::init_log("testout/t1000files.log", 1);
+Imager->open_log(log => "testout/t1000files.log");
 
 SKIP:
 {
@@ -186,6 +186,12 @@ probe_ok(<<JPEG2K, "jp2", "JPEG 2000");
 00 6A 70 32 63 FF 4F FF 51 00 2F 00 00 00 00 01
 JPEG2K
 
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t1000files.log";
+}
+
 sub probe_ok {
   my ($packed, $exp_type, $name) = @_;
 
@@ -5,7 +5,7 @@ use Imager qw(:all);
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t101jpeg.log",1);
+Imager->open_log(log => "testout/t101jpeg.log");
 
 $Imager::formats{"jpeg"}
   and plan skip_all => "have jpeg support - this tests the lack of it";
@@ -20,3 +20,9 @@ ok(!$im->write(file=>"testout/nojpeg.jpg"), "should fail to write jpeg");
 cmp_ok($im->errstr, '=~', qr/format 'jpeg' not supported/, "check no jpeg message");
 ok(!grep($_ eq 'jpeg', Imager->read_types), "check jpeg not in read types");
 ok(!grep($_ eq 'jpeg', Imager->write_types), "check jpeg not in write types");
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t101jpeg.log";
+}
@@ -6,7 +6,7 @@ use Imager::Test qw/is_color3 is_color4/;
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t103raw.log",1);
+Imager->open_log(log => "testout/t103raw.log");
 
 $| = 1;
 
@@ -270,9 +270,13 @@ SKIP:
 	    "check last channel zeroed");
 }
 
-unlink(qw(testout/t103_base.raw testout/t103_3to4.raw
-          testout/t103_line_int.raw testout/t103_img_int.raw))
-  unless $ENV{IMAGER_KEEP_FILES};
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t103raw.log";
+  unlink(qw(testout/t103_base.raw testout/t103_3to4.raw
+	    testout/t103_line_int.raw testout/t103_img_int.raw))
+}
 
 sub read_test {
   my ($in, $xsize, $ysize, $data, $store, $intrl, $base) = @_;
@@ -6,15 +6,18 @@ use Imager::Test qw(test_image_raw test_image_16 is_color3 is_color1 is_image);
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t104ppm.log",1);
+Imager->open_log(log => "testout/t104ppm.log");
 
 my $green = i_color_new(0,255,0,255);
 my $blue  = i_color_new(0,0,255,255);
 my $red   = i_color_new(255,0,0,255);
 
+my @files;
+
 my $img    = test_image_raw();
 
 my $fh = openimage(">testout/t104.ppm");
+push @files, "t104.ppm";
 my $IO = Imager::io_new_fd(fileno($fh));
 ok(i_writeppm_wiol($img, $IO), "write pnm low")
   or die "Cannot write testout/t104.ppm\n";
@@ -46,6 +49,7 @@ i_box_filled($gimg, 20, 20, 130, 130, $gray);
 i_box_filled($gimg, 40, 40, 110, 110, $dgray);
 i_arc($gimg, 75, 75, 30, 0, 361, $white);
 
+push @files, "t104_gray.pgm";
 open FH, "> testout/t104_gray.pgm" or die "Cannot create testout/t104_gray.pgm: $!\n";
 binmode FH;
 $IO = Imager::io_new_fd(fileno(FH));
@@ -225,6 +229,7 @@ is($ooim->tags(name=>'pnm_type'), 1, "check pnm_type tag");
   $im->box(filled => 1, xmin => 8, color => '#FFE0C0');
   $im->box(filled => 1, color => NC(0, 192, 192, 128),
 	   ymin => 8, xmax => 7);
+  push @files, "t104_alpha.ppm";
   ok($im->write(file=>"testout/t104_alpha.ppm", type=>'pnm'),
      "should succeed writing 4 channel image");
   my $imread = Imager->new;
@@ -254,6 +259,7 @@ is($ooim->tags(name=>'pnm_type'), 1, "check pnm_type tag");
   $im->box(filled => 1, xmin => 8, color => '#FFE0C0');
   $im->box(filled => 1, color => NC(0, 192, 192, 128),
 	   ymin => 8, xmax => 7);
+  push @files, "t104_alp16.ppm";
   ok($im->write(file=>"testout/t104_alp16.ppm", type=>'pnm', 
 		pnm_write_wide_data => 1),
      "should succeed writing 4 channel image");
@@ -493,6 +499,7 @@ print "# check error handling\n";
   $im->box(filled => 1, xmax => 4, color => '#000000');
   $im->box(filled => 1, xmin => 5, color => '#FFFFFF');
   is($im->type, 'paletted', 'mono still paletted');
+  push @files, "t104_mono.pbm";
   ok($im->write(file => 'testout/t104_mono.pbm', type => 'pnm'),
      "save as pbm");
 
@@ -514,6 +521,7 @@ print "# check error handling\n";
   $im->box(filled => 1, xmax => 4, color => '#000000');
   $im->box(filled => 1, xmin => 5, color => '#FFFFFF');
   is($im->type, 'paletted', 'mono still paletted');
+  push @files, "t104_mono2.pbm";
   ok($im->write(file => 'testout/t104_mono2.pbm', type => 'pnm'),
      "save as pbm");
 
@@ -545,10 +553,12 @@ print "# check error handling\n";
   $data = '';
   ok($im->write(data => \$data, type => 'pnm'),
      "write 16-bit image as 16-bit/sample ppm");
+  push @files, "t104_16.ppm";
   $im->write(file=>'testout/t104_16.ppm');
   my $im16 = Imager->new;
   ok($im16->read(data => $data), "read it back");
   is($im16->tags(name => 'pnm_maxval'), 65535, "check maxval");
+  push @files, "t104_16b.ppm";
   $im16->write(file=>'testout/t104_16b.ppm');
   is_image($im, $im16, "check image matches");
 }
@@ -584,6 +594,13 @@ print "# check error handling\n";
   }
 }
 
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t104ppm.log";
+  unlink map "testout/$_", @files;
+}
+
 sub openimage {
   my $fname = shift;
   local(*FH);
@@ -6,8 +6,9 @@ use Imager::Test qw(test_image_raw is_image is_color3 test_image);
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t107bmp.log",1);
+Imager->open_log(log => "testout/t107bmp.log");
 
+my @files;
 my $debug_writes = 0;
 
 my $base_diff = 0;
@@ -21,10 +22,12 @@ my $img = test_image_raw();
 Imager::i_tags_add($img, 'i_xres', 0, '300', 0);
 Imager::i_tags_add($img, 'i_yres', 0, undef, 300);
 write_test($img, "testout/t107_24bit.bmp");
+push @files, "t107_24bit.bmp";
 # 'webmap' is noticably faster than the default
 my $im8 = Imager::i_img_to_pal($img, { make_colors=>'webmap', 
 				       translate=>'errdiff'});
 write_test($im8, "testout/t107_8bit.bmp");
+push @files, "t107_8bit.bmp";
 # use a fixed palette so we get reproducible results for the compressed
 # version
 my @pal16 = map { NC($_) } 
@@ -32,9 +35,11 @@ my @pal16 = map { NC($_) }
      2ead1b 0000f8 004b01 fd0000 0e1695 000002);
 my $im4 = Imager::i_img_to_pal($img, { colors=>\@pal16, make_colors=>'none' });
 write_test($im4, "testout/t107_4bit.bmp");
+push @files, "t107_4bit.bmp";
 my $im1 = Imager::i_img_to_pal($img, { colors=>[ NC(0, 0, 0), NC(176, 160, 144) ],
 			       make_colors=>'none', translate=>'errdiff' });
 write_test($im1, "testout/t107_1bit.bmp");
+push @files, "t107_1bit.bmp";
 my $bi_rgb = 0;
 my $bi_rle8 = 1;
 my $bi_rle4 = 2;
@@ -62,6 +67,7 @@ ok($imoo->read(file=>'testout/t107_24bit.bmp'), "read via OO")
 
 ok($imoo->write(file=>'testout/t107_oo.bmp'), "write via OO")
   or print "# ",$imoo->errstr,"\n";
+push @files, "t107_oo.bmp";
 
 # various invalid format tests
 # we have so many different test images to try to detect all the possible
@@ -629,6 +635,7 @@ for my $comp (@comp) {
 	   ymin => 8, xmax => 7);
   ok($im->write(file=>"testout/t107_alpha.bmp", type=>'bmp'),
      "should succeed writing 4 channel image");
+  push @files, "t107_alpha.bmp";
   my $imread = Imager->new;
   ok($imread->read(file => 'testout/t107_alpha.bmp'), "read it back");
   is_color3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, 
@@ -658,6 +665,13 @@ for my $comp (@comp) {
   is($size, 67800, "check data size");
 }
 
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink map "testout/$_", @files;
+  unlink "testout/t107bmp.log";
+}
+
 sub write_test {
   my ($im, $filename) = @_;
   local *FH;
@@ -14,7 +14,7 @@ use Imager::Test qw(is_fcolor4);
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t15color.log",1);
+Imager->open_log(log => "testout/t15color.log");
 
 my $c1 = Imager::Color->new(100, 150, 200, 250);
 ok(test_col($c1, 100, 150, 200, 250), 'simple 4-arg');
@@ -16,7 +16,7 @@ BEGIN { use_ok('Imager') };
 
 -d "testout" or mkdir "testout";
 
-init_log("testout/t36oofont.log", 1);
+Imager->open_log(log => "testout/t36oofont.log");
 
 my $fontname_tt=$ENV{'TTFONTTEST'}||'./fontfiles/dodge.ttf';
 
@@ -1,55 +1,50 @@
-BEGIN { $| = 1; print "1..5\n"; }
-END {print "not ok 1\n" unless $loaded;}
+#!perl -w
+use strict;
+use Test::More;
 use Imager;
 
-$loaded = 1;
+eval "use Affix::Infix2Postfix; 1;"
+  or plan skip_all => "No Affix::Infix2Postfix";
+
+plan tests => 6;
 
 #$Imager::DEBUG=1;
 
 -d "testout" or mkdir "testout";
 
-Imager::init('log'=>'testout/t55trans.log');
-
-$img=Imager->new() || die "unable to create image object\n";
-
-print "ok 1\n";
+Imager->open_log('log'=>'testout/t55trans.log');
 
-$img->open(file=>'testimg/scale.ppm',type=>'pnm');
+my $img=Imager->new();
 
-sub skip { 
-    print "ok 2 # skip $_[0]\n";
-    print "ok 3 # skip $_[0]\n";
-    print "ok 4 # skip $_[0]\n";
-    print "ok 5 # skip $_[0]\n";
-    exit(0);
-}
+SKIP:
+{
+  ok($img, "make image object")
+    or skip("can't make image object", 5);
 
+  ok($img->open(file=>'testimg/scale.ppm',type=>'pnm'),
+     "read sample image")
+    or skip("couldn't load test image", 4);
 
-$nimg=$img->transform(xexpr=>'x',yexpr=>'y+10*sin((x+y)/10)') || skip ( "\# warning ".$img->{'ERRSTR'} );
+ SKIP:
+  {
+    my $nimg=$img->transform(xexpr=>'x',yexpr=>'y+10*sin((x+y)/10)');
+    ok($nimg, "do transformation")
+      or skip ( "warning ".$img->errstr, 1 );
 
-#	xopcodes=>[qw( x y Add)],yopcodes=>[qw( x y Sub)],parm=>[]
+    #	xopcodes=>[qw( x y Add)],yopcodes=>[qw( x y Sub)],parm=>[]
 
-print "ok 2\n";
-$nimg->write(type=>'pnm',file=>'testout/t55.ppm') || die "error in write()\n";
+    ok($nimg->write(type=>'pnm',file=>'testout/t55.ppm'), "save to file");
+  }
 
-print "ok 3\n";
+ SKIP:
+  {
+    my $nimg=$img->transform(xexpr=>'x+0.1*y+5*sin(y/10.0+1.57)',
+			     yexpr=>'y+10*sin((x+y-0.785)/10)');
+    ok($nimg, "more complex transform")
+      or skip("couldn't make image", 1);
 
-# the original test didn't produce many parameters - this one
-# produces more parameters, which revealed a memory allocation bug
-# (sizeof(double) vs sizeof(int))
-sub skip2 { 
-    print "ok 4 # skip $_[0]\n";
-    print "ok 5 # skip $_[0]\n";
-    exit(0);
+    ok($nimg->write(type=>'pnm',file=>'testout/t55b.ppm'), "save to file");
+  }
 }
-$nimg=$img->transform(xexpr=>'x+0.1*y+5*sin(y/10.0+1.57)',
-	yexpr=>'y+10*sin((x+y-0.785)/10)') 
-	|| skip2 ( "\# warning ".$img->{'ERRSTR'} );
-
-print "ok 4\n";
-$nimg->write(type=>'pnm',file=>'testout/t55b.ppm') 
-	|| die "error in write()\n";
-
-print "ok 5\n";
 
 
@@ -17,7 +17,6 @@ my @private =
    '^DSO_',
    '^Inline$',
    '^yatf$',
-   '^m_init_log$',
    '^malloc_state$',
    '^init_log$',
    '^polybezier$', # not ready for public consumption
@@ -0,0 +1,34 @@
+#!perl -w
+use strict;
+use Imager;
+use Test::More tests => 6;
+
+my $log_name = "testout/t95log.log";
+
+my $log_message = "test message 12345";
+
+SKIP: {
+  skip("Logging not build", 3)
+    unless Imager::i_log_enabled();
+  ok(Imager->open_log(log => $log_name), "open log")
+    or diag("Open log: " . Imager->errstr);
+  ok(-f $log_name, "file is there");
+  Imager->log($log_message);
+  Imager->close_log();
+
+  my $data = '';
+  if (open LOG, "< $log_name") {
+    $data = do { local $/; <LOG> };
+    close LOG;
+  }
+  like($data, qr/\Q$log_message/, "check message made it to the log");
+}
+
+SKIP: {
+  skip("Logging built", 3)
+    if Imager::i_log_enabled();
+
+  ok(!Imager->open_log(log => $log_name), "should be no logfile");
+  is(Imager->errstr, "Logging disabled", "check error message");
+  ok(!-f $log_name, "file shouldn't be there");
+}
@@ -0,0 +1,19 @@
+#!perl -w
+use strict;
+use Test::More;
+plan skip_all => "Only run as part of the dist"
+  unless -f "META.yml";
+eval "use CPAN::Meta 2.0;";
+plan skip_all => "CPAN::Meta required for testing META.yml"
+  if $@;
+plan skip_all => "Only if automated or author testing"
+  unless $ENV{AUTOMATED_TESTING} || -d "../.git";
+plan tests => 1;
+
+my $meta;
+unless (ok(eval {
+  $meta = CPAN::Meta->load_file("META.yml",
+				{ lazy_validation => 0 }) },
+	   "loaded META.yml successfully")) {
+  diag($@);
+}